misc
extern “C” {} 的作用是让C++ 能够调用C编写的代码;其中,原理是extern “C”让编译器不要将函数参数信息添加到函数名中,因为C不支持函数重载,因此,只有支持函数重载的C++需要将函数参数加入到函数名称中以支持重载功能。 如果函数的声明和定义不在一起,即定义和声明是分开的时候,那么此时函数的默认参数只能写到声明处,而不能写到定义处。 ==对数组名称的引用:== int * const &ptr = array_name
;==或者是==:int (&array)[10] = array_name
==对字面值取引用是编译错误的,除非使用const常引用。==;==特别注意: ==
int &ri_error = 10 ;
const int &ri = 10 ;
int i = 10 ;
int &ri_err = i + 10 ;
const int &ri_ = i + 10 ;
多使用const:因为const不仅能够使用于const变量而且适用非const(函数参数是const的话那么就能接受const和非const两种参数) ==sizeof引用得到的是所指向的变量的大小,而sizeof指针是对象地址即指针本身的大小==。 引用的本质是const类型的指针 ==new[]申请数组内存:== ==可以省略最左边的大小==
int (*pArray234)[3 ][4 ] = new int[3 ][4 ];
int (*pArray12)[2] = new int[2];
int **p = new int* [10];
// 最后一行语句:p是指针类型,指向的目标是(int *)类型;等号右侧,new的是int * 类型的数组,长度为10
使用了new[]的时候注意配合delete[]使用 set_new_handler()似乎可以为本程序设置一个异常处理函数,这样就不需要进行try-catch也能让这个函数去处理异常了。 ==如果希望在new申请内存的时候不会抛出异常(因为内存不足而抛出的异常) ,那么加上nothrow
==
double *ptr = new (nothrow ) double [10 ];
==用好noothrow,这样只需要判断nullptr而不需要捕捉异常! == ==new 和 delete的特性:不仅申请新的内存,还会对对象调用构造器、析构函数进行构造和析构,而malloc和free仅仅只分配内存而不初始化。== 对==两个int进行除法(且想要得到double结果而不是int结果)的时候,要先完成对除数或被除数的double转换再进行除法==,而不是完成了除法之后再转为double。 static_cast<destination_type>(expression_to_be_cast)
:类似C中的类型转换,是对基本类型的转换:
int Int;
int Int2;
double Double;
Int = static_cast <int >(Double);
Double = static_cast <int >(Int);
auto ret = static_cast <double >(Int) / Int2;
char * ptr = static_cast <char *>(malloc (sizeof (char )));
reinterpret_cast<destination_type>(expression_to_be_cast)
:在底层的位置对内存中的变量的位模式进行重新解释,将其解释为目标转换类型的解释方法,即转换为了新的类型。是对二进制形式的重新解释。如:将一个double对应的变量强制重新按照int来解释,这样就是利用该转换将double强制==按照内存结构的角度==来解释为int,实际上没有发生double到int的逻辑转换、数值换算。const_cast<destination_type>(expression_to_be_cast)
:脱去被转换类型的const属性。==目标转换类型必须是引用或指针!==。const可以接收非const赋值而函数参数是非const参数时不能传递const参数否则出错。dynamic_cast<destination_type>(expression_to_be_cast)
:==在一个继承树中实现父类到子类的转换==。用于多态中父类子类之间的转换。命名空间允许嵌套 别忘了std::string重载了’<’ ‘>’运算符 将==数值转换为字符串,使用==to_string()
将==string转换为数值==:stoi stol stoul stof stod,可以通过base参数来控制进制。stol:string to long string to double string to unsigned long ==注意用好类构造函数的列表初始化功能以提高效率==:
class d
{
public :
int foo;
int __size;
d():foo(10 ),__size(1 )
{
// ...
}
}
==列表初始化的顺序与声明顺序有关而与赋值顺序无关== 有默认的拷贝构造函数,但是只执行浅拷贝。拷贝构造函数的形式的固定的:ClassName(const ClassName & foo)
==注意:浅拷贝也是执行了对栈上变量的拷贝:会对栈上的基本对象和class对象都进行拷贝,而对堆上的变量只拷贝指针而不拷贝/未新建对应的堆对象。 实际上浅拷贝是对栈进行了拷贝,当所拷贝的内容与堆无关时,浅拷贝和深拷贝的作用是一样的==。 二段初始化:对一个对象进行初始化,然后再次调用初始化函数进行初始化。 sizeof(class)
:==成员函数并不计入sizeof,并不占用类的存储空间。==一个对象所占的空间大小只取决于该对象中数据成员所占的空间, 而与成员函数无关==。 成员函数不占用类的存储空间的原因是:==类的方法都保存在一个特定的区域内,该区域内存储了各个类都能调用的公用方法,保存了公共函数==。 const成员必须在类的构造函数的初始化列表中进行赋值 const成员函数是指尾后成员函数,而不是指返回const的成员函数 ==注意:const函数可以构成函数重载 == ==const对象只能调用const函数,非const对象优先调用非const函数 ==。 ==由于const修饰符可以构成函数的重载,因此类外定义的(声明和定义分离的)方法,必须在声明和定义都加上const==。否则声明和定义中有一个有const而另一个没有,则实际上是两个不同的函数的重载版本。 可以依靠static来实现单例模式 使用类名和类实例名都可以访问静态变量 static成员不计入类的sizeof大小之内 ==static函数只能访问static成员== ==static函数只能访问static成员,因为static函数调用的时候没有传递this指针 ==。 ==友元不同于成员,友元没有this指针,友元要想访问对象实例则必须传递this指针作为参数,而成员函数则自带this指针==。 ==前向声明==:不完全的类声明,是对类的声明而不是定义,当只需要用到这个类作为返回类型或只用到了这个类的指针或引用,那么就可以使用前向声明而不需要完整的类定义。 友元并不是声明为类的成员,因此并不受public private protect的影响 友元:不具有传递性、不能被继承 注意:==运算符的重载可以通过友元函数重载和函数成员重载这两种方式来实现。 操作符重载:不能新增操作符,不能改变操作符的操作数个数。==并且,所有操作数中必须至少有一个用户自定义类,而不能全部都是基础类型如int,因为这可以防止用户修改基本类型之间的操作符。== ==操作符重载函数有时候可以是全局函数,有时候可以是类内定义的成员函数==。const Foo& operator+(const Foo& a, const Foo& b);Foo_instance.operator+(const Foo& b);
explicit
不允许隐式转换、不允许隐式调用构造函数。==通过operator重载来实现类型转换 ==
class srcClass {
operator DesClass(void )
{
return srcClassConstructor(arg);
}
}
==通过operator重载来实现的class类型之间的转换 ==:转换operator必须是类的方法,==不可以有参数,不可以有返回值(返回对应类的构造器,而构造器无返回)。== 让一个类对象可以像函数那样被调用,可以通过重载()
操作符的方式来实现。 ==RAII==(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C ++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。 ==派生类构造时,可以使用冒号初始化列表来对基类和内嵌对象进行赋值,以直接调用基类的构造函数。== ==注意:派生类构造函数需要使用冒号初始化列表对基类进行构造。== 当类之间的关系是has-a的时候,是聚合关系,不应该使用继承。如汽车不应该继承车轮类,而应该包含车轮类作为其成员。 ==特别注意:==在派生类的构造函数中,应该使用函数的初始化构造列表来调用基类的构造函数完成对基类的初始化==。 ==特别注意 :基类的构造函数是不会被继承下来的==。 ==友元函数不是类的成员,因此友元函数不能被继承。== 如果希望通过子类来调用基类的友元,==可以将子类转换为基类的类型,然后即可调用基类的友元。== 模板和变量一样,如果不同文件之间存在相同名称的变量和模板,那么链接的时候可能会报错,因此模板和变量一样,存在外部变量,外部模板。 可以将虚函数和纯虚函数当作接口。 模版中的typename与class完全等价。 调用模板函数时,需要带上参数类型,即templateFuncName(arg1, arg2); 即不能漏掉参数类型,不能自动通过函数参数来推导出其类型。 模板的类型参数其实是可以自动推导的,因此在模板函数调用的时候可以省略类型参数即省略尖括号。 除了函数模板外,还存在类模板。 构造函数的执行顺序与析构函数的执行顺序相反 ==当基类的成员与派生类的成员重名的时候,可以使用基类名称的命名空间来访问。==BaseClassName::BaseClassMemberName;
==应该将所有的继承都使用public继承 ,当希望使用private继承的时候,将希望被继承的类作为派生类的一个成员,比使用private继承更佳。== 派生类不能访问基类的private成员==。 不同函数内部定义的静态static变量是不同的,同名的函数内静态变量实际上是不同的两个变量。 ==虚基类、虚继承 virtual public
的作用和意义==:当存在多重继承导致的基类重复的时候,会导致多重继承出来的子类多次继承了子类,使之存在多个相同的子类,因此使用virtual继承可以确保即使发生多次继承也能保证不会出现子类重复的问题。 ==多态==:当使用基类的指针指向派生类对象实例时,并且通过该指针==调用虚函数==(非虚函数无多态/动态绑定)时,会调用派生类的函数而不是子类的,即为多态。 ==子类对基类的虚函数的override,可以自由指定访问权限而不需要与子类相同==。 ==纯虚函数:声明中带有virtual并在最后有=0,含有纯虚函数的类为纯虚类,无法实例化,即为接口类==。 ==含有虚函数的类,析构函数也必须声明为虚函数 。原因:在delete父类指针时,如果该指针实际指向子类,那么能够调用到子类的析构函数而不是仅仅只调用了父类的析构函数,实现完整的析构==。 RTTI:运行时类型识别 运算符 typeid 返回包含操作数数据类型信息的 type_ info 对象的一个引用,信息中包括数据类型的名称,要使用typeid,程序中需要包含头文件。其中 type_ info 重载了操作符==,!=分别用来比较是否相等、不等,函数name()返回类型名称。type_info 的拷贝和赋值均是私有的,故不可拷贝和赋值。 ==写析构函数的时候,注意要不要把析构函数写成virtual的== ==注意:如果基类中一个虚函数都没有(包括虚析构函数),那么当基类指针或引用指向子类时,此时typeid只能得到基类类型而不能得到子类类型==。 ==不要将typeid作用于指针,而应该作用于引用或解引用的指针==。 当指针实际上指向的就是基类而不是子类时,如果用dynamic_cast<>将它转换为子类指针则会返回空指针,因为它实际上指向的对象就不是子类指针。 C++多态的实现:虚函数表 ==static函数不可以是虚函数==:编译不通过。 ==构造函数不可以是虚函数==。因为如果构造函数为虚函数的话,它将在执行期间被构造, 而执行期则需要对象已经建立,构造函数所完成的工作就是为了建立合适的对象,因此在没有构建好的对象上不可能执行多态(虚函数的目的就在于实现多态性)的工作。在继承体系中, 构造的顺序就是从基类到派生类,其目的就在于确保对象能够成功地构建。构造函数同时承担着虚函数表的建立,如果它本身都是虚函数的话,如何确保 vtbl的构建成功呢?编译不过。==构造函数中包含虚函数的时候,该虚函数自动失效变成普通的函数==。 ==析构函数中的虚函数也会自动失效,当做普通函数调用,因为在析构过程中,由于继承树中对象可能已经被销毁掉了,因此动态绑定实际上是不必要的,因此析构函数中的虚函数会被当做普通函数执行。== 仅仅只有当显式调用delete的时候才会调用析构函数。 ==有些函数重载,特别是存在二义性的重载,可以考虑使用模板来替代。== 函数模板实际上是用来生成函数的,会对模板进行两次编译:在模板声明的地方进行编译,在模板生成函数后再对生成的函数进行编译。 ==特化模板==
template <typename T> int compare( T &a, T &b)
template <> int compare < const char * >( const char * &a, const char * &b)
类模板的一个很重要的应用:如stack类。设计为模板的时候,该stack类可以接受不同类型的对象进行push/pop。 ==由于函数模板会在被使用的时候进行编译、生成对应类型的函数,因此如果一个模板在多处被引用,如果发生多次函数生成,会导致同一函数存在于多处,造成函数重复定义,因此应该将模板的声明和定义都放到头文件中。== ==一个try可以带有多个catch==。 ==在一个不会抛出任何异常的函数,可以使用nothrow()来表示该函数不会抛出异常,不需要做出对异常的处理等额外工作==。