仓鼠一般搜集到的C++技巧

    1. 通常不应该出现两个头文件循环包含的情况,若两个头文件包含,则说明两个头文件中包含的两个类之间关系高度耦合。即A 需要 B , B 需要 A。依照《重构》中的思想,这种情况属于代码臭味,需要考虑将这两个类中的内容合并为一个类,之后将不存在循环包含的情况。类之间通常是单向依赖关系。
    1. 当前阶段先学会自己写出makefile。
    1. 类的静态成员要在类外进行初始化。
    1. 左值表达式表示对象的身份,右值表达式表示对象的值。返回左值引用的函数,连同赋值、下标、解引用、和前置递增递减运算符都是返回左值表达式的,因此可以将左值引用绑定其上。返回非引用类型的函数、算术运算符、关系运算符、位运算符、以及后置递增递减运算符,都生成右值,我们可以将一个const左值引用或者一个右值引用绑定其上。右值表示一种短暂的临时的引用。
    1. 移动构造函数不创建任何新内存,而是接管旧对象的成员,因此在移动构造函数中需要使得旧对象失去对其成员的所有权。
    1. 头文件中只写声明,不写定义,定义写在C文件中,这个挺好理解的。我把全部的定义写在头文件中,然后cpp文件只include一下,main.cpp不改动,然后依次编译出obj文件,没有问题,但是链接时显示函数重复定义。而改回头文件中只写声明的方式,把类外定义全部移到c文件中是没有问题的,好像理解这个出错的原因,但是又觉得头文件加了保护符不应该会这样啊。先记下这个问题。已经解决了,定义可以重复,但是定义不可以重复,都写在头文件中是,与处理步骤会把头文件替换到两个CPP文件中,然后编译时各自的符号都可以找到没有问题,但是在链接步骤就会发现重复的函数定义。而两个CPP文件分属两个编译单元,头文件保护符不管用。
    1. 对于上面的问题还有一种解决办法,就是也可以把类外定义全部写在头文件中,然后cpp文件只include一下(实际上此时的cpp文件可以不要),然后把头文件中的类外定义全部使用inline方式,这样也没有问题了。因为内联函数在实际调用时进行代码替换而不是函数定义的查找,inline实际上是c++中对宏定义的一种替代。这样也就使得不会在链接阶段出现重定义的错误。类中定义的实际上都是inline方法,而且inline也必须写在头文件里,因为需要随处展开的原因,声明处必须要看得见定义,如果非要写在cpp文件里,那就在头文件里include对应的cpp文件。
    1. 在map中使用下标访问不存在的元素将导致在map容器中添加一个新的元素。
    1. 在循环内可以声明变量,每次变量的生命周期只有一次循环,结束了变量就释放了,下一个循环又是定义一个新的变量,没有问题。
    1. 偶然看到的“菜鸟级”编程规范,h文件只包含在cpp文件里,包含在h文件中会导致层层递归,依赖很深。而且,不然在头文件中使用using namesapce,由于头文件会在预处理阶段进行代码替换,这可能导致这个namespace的影响被扩散的很远。参考别再让头文件中出现‘using namespace’
    1. 编译过程形成的目标文件会提供三张表:导出符号表、未解决符号表、地址重定向表。很多知识点可以映射到这三张表上去,比如头文件中的inline函数被多次包含不会有符号冲突,是因为内联是进行替换,不形成外部符号表,对外不可见;再比如extern关键词,其实就是告诉编译器,要把这个变量符号放到未解决符号表中,去找别的obj文件链接。
    1. 只有一个实参的构造函数可以进行隐式的类型转换,explicit关键字使得这种构造函数不能隐式的调用。
    1. 不知道怎么声明友元是最佳实践,但是C++Primer中给出的示例代码是在头文件中,类内给出友元函数的声明,这部分是权限说明;然后再类外进行友元函数自己的函数声明;然后在具体的CPP文件中给出函数定义。可以是头文件对应的CPP文件,也可以是单独的工具函数CPP文件。这个具体问题具体分析。
    1. 析构函数不能显式调用。
    1. sizeof是在编译的时候求值的,如果是一个对象,那么sizeof返回该对象的的数据成员所占的空间大小。string是一个类,真正的内容是在堆中的,然后string对象有指针指向真正的字符串,所以对string进行sizeof的话得到的就是string类的数据成员占得空间大小。在我的机器上就是32。实际取得字符串的字节长度要用size方法或者length方法。
    1. stack上的对象的析构函数自动调用,heap上的对象的析构函数需要手动delete来调用。
    1. 不要返回函数内的局部变量的引用或者指针,除非这个变量指向的是堆中的内存。
    1. 构造函数中不能加虚函数,因此构造过程中vtable还没有建好,虚函数不能动态绑定,只能执行基类自己的虚函数。
    1. union 中的元素共享内存。
    1. 如果一个函数接受const类型的参数,但是传入的是非const的,会发生什么?
    1. 对有基类的对象来说,基类的成员初始化和构造函数体的执行发生在派生类的成员初始化和构造函数体的执行之前。
    1. 请注意 static 类成员永远也不会在类的构造函数初始化。静态成员在程序运行的过程中只被初始化一次,所以每当类的对象创建时都去“初始化”它们没有任何意义。
    1. 对一个对象的所有成员来说,它们的析构函数被调用的顺序总是和它们在构造函数里被创建的顺序相反。
    1. numeric_limits(和标准库中其它东西一样,numeric_limits 存在于名字空间 std 中; numeric_limits 本身在头文件中)。
    1. 在定义派生类时, 派生类并没有把基类的构造函数继承下来。因此, 派生类的构造函数同时需要对继承的基类成员进行初始化。派生类并没有继承基类的析构函数, 在执行派生类的析构函数时, 编译器会自动调用基类的析构函数。核心原则是构造顺序和析构顺序相反。
    1. 关于“指针常量”和“常量指针”很好区分:const 在星号左边,则被指物是常量;const 在星号右边,则指针自身是常量(此时const关键字和类型关键字的顺序不重要)。
    1. 声明只是告诉编译器这个变量会被使用,编译器不(保证)为其分配内存;而定义是一定会有内存被分配的。因此,extern关键字一定是声明,带有初始化的一定是定义。参考你可能不知道的定义、声明和初始化
    1. 编译单元就是产出单一目标文件的所有源码。
    1. static对象,其寿命从构造出来直到程序结束为止,因此这种对象不是堆和栈中存放的对象。这种对象包括global对象,namespace中定义的对象以及使用static关键字的对象。在函数内定义的静态对象是局部静态对象,其他的(包括类内的)静态对象都是非局部的。
    1. 空类的大小是1。原因是C++标准要求类的大小必须为正,这是为了保证实例化得到的不同对象的地址不能相同。(这句话还不太明白)
    1. 对于类内的 const 类型和 static 对象的初始化:const 类型可以在声明处初始化,也可以在成员初始列中初始化,后者会覆盖前者。static 类型必须在类的声明体外定义。而 const static 对象支持在声明处初始化,也可以像普通的 static 变量一样,在类外定义与初始化。
    1. 当执行new的时候,发生了两件事:一块内存被分配出来,然后一个或多个构造函数被调用。析构函数同理。
本站总访问量