枚举 语法 enum test { one=0 ,two,three=10 };
类似于类,其中,每个枚举值都代表了一个unsigned int,但是你无法将一个unsigned int的值赋给这样一个枚举类型。
此外,定义枚举时,每个枚举变量都可以自己设计值,无值的为前一个枚举量加1
int main () { int a = 1 ; test s (one) ; test a (static_cast <test>(a + s)) ; s = test (1 ); }
其构造函数必须接受一个枚举的对象,但我们可以通过强制类型转换实现赋值。
应用 int main () { test a=one; witch (a) { case one: ... break ; case two: ... break ; } }
强制类型转换 显示 static_cast static_cast :主要用于基本类型的转换,如int 到double ,以及在类层次结构中的指针或引用的转换。 它能够执行安全的转换,如果转换不合法(例如,从子类指针转换为基类指针,但实际对象不是那个基类的实例),编译器会报错。 不适用于解除const 约束,如果尝试解除const ,编译器会发出警告或错误。 如上转换int 到enum 时所示例子,转换的两者必须要有关联,要么存在默认的类型转换(子类),要么就是存在定义好的类型转换函数,一般都可以绕过此方法而使用类型转换;
const_cast const_cast :专门用于添加或移除const 或volatile 属性。 可以用来改变指针或引用的const 属性,但并不能改变实际对象的const 属性。试图通过const_cast 修改const 对象的行为是未定义的,可能会导致运行时错误。一般的,用于将只能找到指针的对象的const 属性去除,得到一个非const 的指针,以便对地址中的数据进行修改(该地址的数据不能是const ) int main () { int a=0 ; const int *b=&a; int *c=const_cast <int *>(b); }
dynamic_cast dynamic_cast :用于运行时多态性,通常与虚函数一起使用。 它可以将指针或引用从基类转换为派生类,如果转换成功,返回有效指针;如果转换的对象不是目标类型,返回nullptr 。 只适用于指向具有虚函数的类的指针或引用。 用于将基类对象的指针安全的转换为子类的指针 #include <iostream> #include <vector> class Base {public : virtual ~Base () {} virtual void print () { std::cout << "Base" << std::endl; } }; class Derived1 : public Base {public : void print () override { std::cout << "Derived1" << std::endl; } }; class Derived2 : public Base {public : void print () override { std::cout << "Derived2" << std::endl; } }; int main () { std::vector<Base*> objects; objects.push_back (new Derived1 ()); objects.push_back (new Derived2 ()); for (Base* basePtr : objects) { Derived1* derived1Ptr = dynamic_cast <Derived1*>(basePtr); if (derived1Ptr) { derived1Ptr->print (); } else { Derived2* derived2Ptr = dynamic_cast <Derived2*>(basePtr); if (derived2Ptr) { derived2Ptr->print (); } else { std::cout << "Not Derived1 or Derived2" << std::endl; } } } for (Base* obj : objects) { delete obj; } return 0 ; }
reinterpret_cast reinterpret_cast: 最为灵活,但也是最危险的转换。 它可以将任何指针或整数类型转换为另一个,甚至可以无视类型系统,执行底层的位转换。 这种转换通常用于低级别的操作,如将指针转换为整数,然后再转换回去,或者在不兼容的指针类型之间转换。它不保证结果有意义,因此应谨慎使用。
隐式 默认
当派生类的对象被赋值给基类的对象时,会发生隐式转换。
当派生类的对象传递给函数参数为基类的引用或指针时,也会发生隐式转换。
其他
void ew (int c) { cout<<c; } ----------------------------------------------------- int main () { char s='1' ; ew (s); } ----------------------------------------------------- int main () { int a=10 ; double b=a; } ----------------------------------------------------- class test { explicit test (int ) } 此处限制显式转换,即只能通过强制类型转换来完成,不能通过赋值语句来完成。 ----------------------------------------------------- 等诸如此类的类型转换
定义 类可以定义非显式的转换函数(通常为成员函数),使其对象能够被隐式或显式地转换为其他类型。 使用关键字operator 定义,如operator double () const { return this ->value; }可以让类实例能够被当作double 使用。 class t { public : int a; int b; virtual void re () { cout << 1 ; } string operator string () { char empt1 = static_cast <char >(a); char empt2 = static_cast <char >(b); std::string str (1 , empt1) ; str += std::string (1 , empt2); str += std::to_string (static_cast <int >(empt2)); return str; } };
String 相关操作 getline getline (cin, b,';' );此函数比较常见
复制 std::string str1 = str2;:使用复制构造函数创建str1作为str2的副本。 std::string str3 (str2) ;:同上,使用复制构造函数。str1.assign (str2);:将str2的内容赋值给str1。
剪切/子串提取: std::string substr (size_t pos = 0 , size_t n = npos) const ;:提取从pos位置开始的n个字符的子串。默认到字符串末尾。
删除: string& erase (size_t pos = 0 , size_t n = npos) ;:删除从pos开始的n个字符。默认删除到末尾。
查找: size_t find (const string& str, size_t pos = 0 ) const noexcept ;:查找str第一次出现的位置,从pos开始。未找到返回npos。类似的有find_first_of, find_last_of, find_first_not_of, find_last_not_of等
插入: string& insert (size_t pos, const string& str) ;:在pos位置插入str。也可以插入单个字符或字符数组。
替换: string& replace (size_t pos, size_t n, const string& str) ;:从pos开始的n个字符被str替换
拼接: string& operator +=(const string& str);:将str追加到字符串末尾。 string operator +(const string& str1, const string& str2);:返回str1和str2的拼接字符串。
大小和容量: size_t length () const noexcept ; 或 size_t size () const noexcept ;:返回字符串长度。size_t capacity () const noexcept ;:返回当前容量。void resize (size_t n, char c = char ()) ;:调整字符串大小为n,可选填充值c填充新空间。
对应指针 file.write (text.c_str (), text.size ()); 此函数得到const 指针 &text[0 ]为非const 指针
模板 函数模板 template <typename T>T add (T a, T b) { return a + b; } int main () { char a,b; int z,x; double q,w; add (a,b); add (z,x); add (q,w); }
使用模板时,编译器会根据实参的类型来确定模板的实参类型,从而生成对应的函数。
形式 T add (vector<T> a, T b) { return b; }
传入引用 template ,用typename指代函数的参数类型,T是模板的形参。 使用范围仅限紧跟着的第一个函数声明,后续的函数声明不能使用模板。若使用模板但不使用,则需要强制类型转换才能调用这个函数,如:
填入的所有元素必须保持一致。
类型转换 类型转换是模板的一大考点:
隐式类型转换: 即类似于char和int的转换
char a='a' ;void sd (int ) ;sd (a);
强制类型转换: 即
int a=1 ;char b='a' ;b=(char )a; 强制类型转换,需要强制类型转换符,如:(char )a。
体现在模板上,就是
T add (vector<T> a, T b) { return b; } add <int >(a,b);
重载 可以实现重载 当函数模板和普通函数重名时,编译器会优先选择函数模板,除非使用空模板参数列表。
template <typename T>T add (T a, T b) {} int add (int a, int b) {} int main () { add<>(int a=1 ,int b=0 ); } <>记为空模板参数列表。
特化 template <>add (person a,person b){ if (a.?==b.?) return a; else return b; } 貌似没什么用
类模板 template <class AH ,class sd >class person { public : AN s; sd a; person (AH s,sd a); } int main () { person<int ,int > a (1 ,2 ) ; }
用于创建类的模板,类模板的类名与模板名相同,类模板的类名后接尖括号,尖括号内为模板形参列表。
类型 类模板不会自动类型推导
template <class AH >void nam (AH a) { a.pp (); }
麻烦的东西,狗都不看 需要的时候直接把模板类的声明填进去就完事了。
继承 模板类的继承
template <class AH >class person { public : AH s; person (AH s); } class son () :public person<int>继承时必须强制类型转换 否则,子类也必须是模板类 如: ```c++ template <class AH> class son():public person<AH>
现在,用户将类传入模板类person,person带着参数传入模板类son。
类型 std::type_info 可以通过 typeid 运算符获取对象的类型信息。typeid 返回一个指向 std::type_info 对象的常量引用。 #include <typeinfo> #include <iostream> int main () { int num = 10 ; const std::type_info& type = typeid (num); std::cout << "Type of num: " << type.name () << std::endl; return 0 ; } 在上面的示例中,typeid (num) 返回 std::type_info 对象,表示变量 num 的类型信息。type.name () 可以获取类型名称的字符串表示。
迭代器 输入迭代器 只允许读取迭代器的内容,遍历(++)后找到/找不到需求值,返回其索引,反之返回超尾或空值。
输出迭代器 输出指定区域的所有值而不需要对其进行修改
std::ostream_iterator <int >(std::cout, " " ) 同cout输出,附带一个“ ”
正向迭代器 可读也可写
双向迭代器 随机访问迭代器 需要operator+
迭代器或许是一种概念,一种方法,一种单位,一个小区域,方法为这些区域指定找到他们的途径。
异常 try-catch #include <iostream> using namespace std;int main () { try { int x = 10 ; int y = 0 ; if (y == 0 ) throw y; int result = x / y; } catch (int e) { cout << "捕获到除数为零的异常: " << e << endl; } catch (...) { cout << "捕获到未知类型的异常" << endl; } cout << "程序继续执行..." << endl; return 0 ; } 一旦发现错误,如y==0 ,抛出错误,内容为int 类型的y,其值为0 ;此时退出当前函数或域,寻找第一个匹配的错误类型,随后执行其内部的语句
友元 友元类
友元函数
主要注重于友元函数叙述
class person { friend istream& operator >>(istream& in, person& a); friend person operator +(person& a, person& b); private : int age; public : person (int a = 10 ) { age = a; } person operator +(person& a); person operator -(person& b); bool operator ==(person& a); bool operator >(person& a); }; person person::operator +(person& a) { cout << "in" ; person empt; empt.age=this ->age += a.age; return empt; } person operator +(person& a, person& b) { cout << "out" ; person empt; empt.age = a.age + b.age; return empt; } istream& operator >>(istream& in, person& a) { in >> a.age; return in; } person& person::operator ++() { this ->age++; return *this ; } person person::operator ++(int ) { person empt (*this ) ; this ->age++; return empt; } 一般的,+,-,==,>等既可以声明为友元,,也可以不这样做。 同时声明为友元和函数成员会引发报错。 istream& operator >>(istream& in, person& a)的确可以声明为成员函数,但这需要更改函数签名
类 const 存在指针常量,常量指针,指针常量指针。 存在 const int sd();
int sd(const int);
int sd()const; 此处const表明此函数可以为常量对象调用,其中成员变量无法被修改,除非此成员变量为静态变量或被mutable修饰过mutable int a; int b; void sd () const { a = 10 ; } void ss () const { sd (); }
重載 大部分上述友元已經介紹 1. 操作符重载函数不能为静态成员函数
静态数据成员 class t { public : static int tty; t () { a = 0 ; tty++; } int a; protected : int b; private : static int ccy; int c; }; class t1 :public t{ public : t1 () { tty++; cout << ccy; } }; int t::tty = 0 ;int t::ccy = 0 ;int main () { t1 a; cout << t::ccy; } 静态数据成员在己类和子类共用,但也分访问权限。与普通成员一样,只不过不需要对象来调用。
静态成员函数 class t { public : t () { a = 0 ; } int a; static void tochar (t*) ; protected : int b; private : int c; }; class t1 :public t{ public : t1 () { } }; void t::tochar (t* a) { a->c++; } 静态成员函数是所有对象公用的一个接口,类似于工具,他不可能存在this 这种宣誓主权的东西,他可以对传入的对象的数据进行修改,可以在类内声明,后果是无法在外界使用这个函数,也不能通过对象调用。
其他成员 类型转换 string operator string () { char empt1 = static_cast <char >(a); char empt2 = static_cast <char >(b); std::string str (1 , empt1) ; str += std::string (1 , empt2); str += std::to_string (static_cast <int >(empt2)); return str; }
拷贝 class t { public : t (int c=0 ) : a (c) {} ~t () { cout << 1 ; } t (t& q) { a = q.a; cout << "ddd" ; } private : int a; }; t dd (t a) { return a; } int main () { t a (2 ) ; dd (a); } 此函数运行了两次,一次是传入函数,第二是传出函数,都通过此方法构造了一个副本。 由此引发的深拷贝需要根据情景来看
继承 四种继承,public protect private是各自具备一个父类为自己的一个可能的对象,但必然存在重叠部分而不能是视为完全的对象。virtual继承将子类的父类化为共用的一个,因此而引发考点
p继承 既然类似于对象作为数据成员,那么初始化必不可少,由此,构造函数是无法继承而来的。 存在考点: 1.生成以及销毁的顺序。 2.作用域下的同名成员 3.3p各自的特点 4.虚函数
v继承 别名菱形继承,所以共有三层,第一层为基类,第二层为v继承的各个子类,第三层为这些子类共同的孙类。 所有类共用一个基类,故此基类只需要构造一个即可 using namespace std;class A { public : double x, y; A (double px = 1 , double py = 1 ) :x (px), y (py) { cout << "a" ; show (); } virtual ~A () { } virtual void show () { cout << "[A]=>" << x << "," << y<< endl; } }; class B1 : virtual public A { public : B1 (double px = 2 , double py = 2 ) :A (px, py) { cout << "B1" ; show (); } void show () { cout << "b1" << x << y; } }; class B2 : virtual public A { public : double z; B2 (double px = 3 , double py = 3 , double pz = 3 ) :A (px, py), z (pz) { cout << "B2" ; show (); } void show () { cout << "b2" << x << y << z; } }; class C : public B1, public B2 { public : double h; C (double px = 4 , double py = 4 , double pz = 4 , double ph = 4 ) :B2 (px, py, pz),B1 (px,py),A (px,py), h (ph) {} void show () { cout << "c" << x << y << z << h; B1::show (); B2::show (); A::show (); } }; int main () { C c; c.show (); }
由此引发的是C的构造函数中必须构造A,否则A的值为默认值。而一旦A被正确构造,那么b1,b2中的值也都会同步改变。
多态 虚函数 虚函数不要求子类重写同名函数,无影响。
纯虚函数 纯虚函数要求子类必须重写,无法显示创建对象,但其子类调用其构造函数没有问题。
虚析构 class t { public : t () : a (0 ) {} virtual ~t () { cout << 1 ; } private : int a; }; class t1 : public t{ public : t1 () : t () {} ~t1 () { cout << 2 ; } }; int main () { t *a= new t1; delete a; } 输出结果为2 1 ; 由此得到虚析构的作用:释放空间更彻底
纯虚析构 用来强制其子类写一个析构函数,强制其安全规范了
文件 文本文件 写文件 #include <fstream> 头文件 创建流对象 ofstream ofs;写 fstream fs;读写 ofs.open ();打开文件 或者if (file),隐式调用 void * 转换运算符,检查流状态,void *为通用指针类型,可以用static_cast 转换。 如ofs ("abc.txt" ,ios::out) 或ofs ("abc.txt" ,ios::out|ios::ate)(这里复用) 如果没有abc文件,则创建 ofs<<"..." ;写入文件 ofs.close ();关闭文件
ios::in
为读文件而打开文件
ios::out
写文件(覆盖)
ios::ate
在文章尾写文件
ios::app
追加方式写文集
ios::trunc
删除文件再创建
ios::binary
二进制方式
读文件 #include <fstream> 头文件 创建流对象 ifstream ifs;读 ifs.open ("abc.txt" ,ios::in);如上 if (ifs.is_open ())接着判断,如果有则返回1 { ifs>>...输入流>>,输出流<< } while ( (c=ifs.get ())!=EOF){ }表示读到结尾结束 ifs.close ();
相关函数操作 ifs.seekg (36 *(a-1 ),ios_base::beg);用于写操作 ofs.seekp (36 *(a-1 ),ios_base::beg);用于读取操作 用于移动文件指针到36 (a-1 )的位置,即跳过36 *(a-1 )个元素在进行输入。 getline (ifs,str,'\n' );读取一行,直到遇到换行符(\n)。输入的对象必须是string类型,即例子中的str。可以指定停止读取的位置,默认为'\n' 。 ios_base::beg:从文件开始处计算指针位置 ios_base::cur:从当前位置计算指针位置 ios_base::end:从文件末尾计算指针位置 ifs.tellg ();获取文件指针位置 ifs.peek ();查看文件指针位置 ifs.eof ();判断文件是否结束 ifs.clear ();清空错误 std::ios::goodbit:无错误。 std::ios::eofbit:到达文件末尾。 std::ios::failbit:发生格式错误或提取操作失败。 std::ios::badbit:发生不可恢复的错误。 可以通过流对象的成员函数(如good ()、eof ()、fail ()和bad ())来检查这些状态位。 #include <iostream> #include <fstream> int main () { std::ifstream file ("example.txt" ) ; if (file.good ()) { std::cout << "No errors, file is good." << std::endl; } while (file) { char ch; file.get (ch); } if (file.eof ()) { std::cout << "Reached end of file." << std::endl; } if (file.fail ()) { std::cout << "Logical error on input operation." << std::endl; } if (file.bad ()) { std::cout << "Read/writing error on i/o operation." << std::endl; } file.close (); return 0 ; }
二进制文件 int main () { std::cout << readText << std::endl; std::string text = "Hello, World!" ; std::ofstream file ("textfile.bin" , std::ios::binary) ; file.write (text.c_str (), text.size ()); file.close (); ifstream inputFile ("textfile.bin" , std::ios::binary) ; std::string readText; inputFile.seekg (0 , std::ios::end); std::streamsize size = inputFile.tellg (); inputFile.seekg (0 , std::ios::beg); readText.resize (size); inputFile.read (readText.c_str (), size); inputFile.close (); }
文件后缀为bin,写入用write(const char*,size) 读取用read(char *,size) 此处相关操作与string绑定
I/O操作 cerr clog cerr是非粘滞性的,clog,cout,cin是粘滞性的,即cerr不经过缓冲区,而clog,cout,cin经过缓冲区。 缓冲区是存储在计算机内存中的,当缓冲区满时,会自动写入到文件。 设置缓冲区可以防止多次进行昂贵的I/O操作
cout cin cout.put (char ); 单独输出一个字符 cout.ignore (int ,'!' ); cin.ignore (int ,'!' ); 用于忽略int 个字符,直到遇到字符'!' ,即忽略'!' 之前的字符。
iomanip 1. setbase、hex、dec、octsetbase:用于设置整数输出的基数(如十进制、十六进制或八进制)。这是一个非黏性的操纵符,只对紧接着的一次输出操作生效。 std::cout << std::setbase (16 ) << 255 << std::endl; std::cout << 255 << std::endl; hex、dec、oct:分别用于将整数输出设置为十六进制、十进制和八进制。这些操纵符是黏性的,会持续影响后续的输出操作,直到显式地改变设置。 std::cout << std::hex << 255 << std::endl; std::cout << 255 << std::endl; std::cout << std::dec << 255 << std::endl; 2. precision、setprecisionprecision(成员函数):设置输出流中浮点数的精度,这是一个黏性的操作。 std::cout.precision (5 ); std::cout << 3.14159265 << std::endl; std::cout << 2.71828 << std::endl; setprecision(操纵符):同样用于设置浮点数的精度,也是一个黏性的操作。 std::cout << std::setprecision (3 ) << 3.14159265 << std::endl; std::cout << 2.71828 << std::endl; 3. width、setwwidth(成员函数):设置输出流中字段的宽度,这是一个非黏性的操作,只对紧接着的一次输出操作生效。 std::cout.width (10 ); std::cout << 42 << std::endl; std::cout << 42 << std::endl; setw(操纵符):设置输出字段的宽度,也是一个非黏性的操作。 用十位空位填充,数字从后往前替换,超出了则视为stew失效 std::cout << std::setw (10 ) <<setfill ('n' )<< 42 << std::endl; std::cout << 42 << std::endl; 常添加setfill ('n' )用来填充