- 声明和定义:函数的声明和定义区别比较简单,带有{ }的就是定义,否则就是声明。extern是声明不是定义,带有初始化的声明必定是定义,即便前面加了extern,但是extern只有位于函数外部时,才可以被初始化,除非有extern声明,否则都是变量的定义。定义分配存储空间,声明仅仅表明变量类型和名字。
- cout输出char数组时,是通过'\0’字符来判断结束的,与数组长度无关,可短可长。
sprintf(buf_sec, "%08X", tv.tv_sec)
输出字符长度最小为8,如果输出的值短于8,前面用0填充,长于8不会被截断。
- 递增或递减运算对于用户自定义的类型,前缀效率更高,因为后缀格式需要先复制一个副本,将其加1,然后将副本返回,因此,对于类而言,前缀版本效率更高。副作用和顺序点(下一条语句之前,所有修改都必须完成)
- 循环内部声明变量每次都要分配和释放,相对于外部声明速度要慢一些,如果循环很大的话。
const int *p = &a, int * const p = &a
前者不能通过*p修改变量a的值,但可以改变指针p,使其指向别的地址,后者可以改变*p的值,但不能改变指针p。
- 在某个函数中new了一块内存,在该函数执行结束后,该函数中指向该内存的指针变量会被释放,但这个内存仍然可用,除非手动delete。
- 函数执行时间很短,且经常被调用,可考虑使用内联函数,内联函数是用函数代码替换函数调用(但需要更多的内存),所以不能递归。
- 引用变量适合struct和class。(需要再仔细读一遍。。。)
- C++模板不能缩短程序,最终程序仍是由具体函数组成的,最终的代码也不包括模板,只包含了为程序生成的实际函数,模板只是使得多个函数定义更简单。
- 函数原型:非模板版本>显示具体化版本>模板版本。(章节8.5.5:编译器选择使用哪个函数版本)
- decltype是个好东西,和模板结合使用很不错。
- 不要将函数定义或变量声明放到头文件中,这样如果有两个文件中包含该头文件,则同一个程序中将包含同一个函数的两个定义,除非函数是内敛的,否则这将出错。
- 头文件常包含的内容:函数原型、使用#define或const定义的符号常量、结构声明、类声明、模板声明、内联函数。
- #include头文件加双引号,则编译器首先在当前工作目录或源代码目录查找头文件,没找到再去标准位置找,而尖括号是直接去标准位置找,所以尽量用双引号。
- 使用#ifndef可以防止将某个头文件包含多次。
- 每个对象都有自己的存储空间,用于存储内部变量和类成员,但同一个类的所有对象共享同一组类的方法。即每一个对象有自己 的内存块,但代码块只有一个。
- stock=Stock(1,"a");类似这样的对象赋值,程序会先创建一个临时对象,然后将内容复制给目标对象,最后删除该临时对象,所以效率很低,几乎不用。
- const成员函数:void show() const;/void Stock::show() const。const成员函数表示该函数不会对调用该函数的对象进行修改,const object &指向const的引用表示不能对引用的对象进行修改。
- 返回对象会调用复制构造函数,但返回引用不会。不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据,此时必须返回对象,即便会增加开销。(p451)
- 3种内联函数:直接在类声明里面定义的(无论有没有inline关键字),在类里面inline关键字显示声明但是在类外面定义的,类里面没有显示inline声明但是类外面显示定义了inline内联。注意:内联函数中不能有循环语句和switch语句,并且最好不要超过5条语句。
- 友元函数和成员函数有相同的访问权限。友元函数和成员函数在重载运算符时的区别是,友元函数可以让运算符的两个操作数都成为参数,但成员函数则是用类对象来调用,只有一个参数。友元函数在重载<<运算符时很有用,trip是一个类的实例,cout << trip <<" "<< endl用非友元函数则很难实现重载,但是用友元函数重载就很方便。重载运算符格式operatorop(argument-list)(p391)。
- 静态数据成员(static)的特点是,无论创建了多少对象,程序都只创建一个静态类变量副本,在类声明中声明,在包含类方法的文件中初始化,之所示在类方法文件中初始化,是因为程序可能将头文件包含在其他几个文件中,如果在头文件初始化,将出现多个初始化副本,引发错误,但是如果静态类成员是整型或枚举型的const,则可以在类声明中初始化,例如steed中config.h(p428)。
- 复制构造函数。默认的复制构造函数会逐个复制非静态成员的值,并且对于对于分配过内存的指针,会复制相应的指针,即两个对象会指向同一块内存,很容易导致出错。最好自己显示的定义一个复制构造函数,以复制指向的数据。StringBad ditto(motto),StringBad ditto=motto, StringBar ditto=StringBad(motto), StringBad* ditto=new StringBad(motto)都会调用复制构造函数。其中中间两种还可能使用赋值运算符,所以最好自己定义赋值运算符。(p433)
- 静态成员函数声明需要static,定义不需要,不能通过对象调用,只能使用静态类成员。(p441)
- 构造函数使用new(new[]),析构函数必须使用delete(delete[]),但一般不用new [],因为是在堆上分配内存。
- 在类声明中声明的嵌套类、结构或枚举作用域为整个类,这种声明不会创建对象,只是指定了类型,如果声明时private,则只能在这个类中使用,如果是public,则可以在类外部使用作用域解析运算符使用。(p463)
- const类成员和引用的的类成员初始化时除了声明初始化外,必须使用成员初始化列表的方式来初始化,构造函数后面冒号加列表,多个参数逗号隔开,这种方法的一个好处是可能会减少赋值运算符的开销。(p464)
- 程序在构造派生类对象时。会首先创造基类对象,调用基类的构造函数(如果未定义则调用默认的基类构造函数),然后才调用派生类构造函数,派生类对象(注意是派生类对象而不是指向或引用派生类对象的基类对象)过期时,首先调用派生类析构,然后调用基类析构。
- 派生类不能直接访问基类的私有成员,必须通过基类的方法访问,构造函数同理。(p484)
- 派生类不能调用基类的私有方法。基类指针(引用)可以指向(或引用)派生类对象,但是基类指针或引用只能调用基类的方法,这是单向的,即不能将基类指针或地址赋给派生类指针或引用。(p488)
- 如果在派生类中重新定义基类的方法,通常应将基类方法声明为虚的,这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本。另外为基类声明一个虚析构函数也是一个好的习惯,这样可以保证正确的析构顺序,因为如果不是虚析构函数,则析构会根据指针或引用的类型来调用析构函数,这样当指针或引用的类型是基类,但对象是派生类时仍然会调用基类的析构函数,可能会导致错误。(virtual关键字只用于声明中,不用于定义)。
- 类对象最好不要按值传递。
- 构造函数不能是虚函数,因为创建派生类对象时,将调用派生类的构造函数,然后派生类构造函数将使用基类的构造函数,这种顺序不同于继承机制。友元也不能是虚的,因为友元不是类成员,只有成员函数才能是虚函数。
- 继承时重新定义相同的函数名但特征标不同的函数将隐藏同名的基类方法,因此若要重新定义继承的方法,应确保原型相同,但若返回类型是基类指针或引用,则可以修改为指向派生类的引用或指针。
- 虚函数工作原理-虚函数表。(p504)
- 保护数据成员(protected)可以让派生类直接访问,但最好不要使用,然而保护成员函数是比较有用的。
- ABC: abstract base claa(抽象基类)
- 原型中加上=0表明该类是一个抽象基类,虚函数声明后面加上=0表明这是一个纯虚函数,类中包含纯虚函数时,不能创建该类的对象。
- 模板类若包含参数,需注意参数的限制较多,参数是整型,枚举,引用或指针。模板代码不能修改参数的值,不能实用参数的地址,用作参数的值必须是常量表达式。(p578)
- c++四种cast类型转换:
- Static_cast:能完成大部分转换功能,但是并不确保安全。
- Const_cast:无法从根本上转变类型,如果是const,它就依旧是const,只是如果原对象不是const,可以通过此转换来处理,针对指针和引用而言。
- Dynamic_cast:针对基类和派生类指针和引用转换,基类和派生类之间必须要继承关系,是安全的。
- Reinterpret_cast:允许将任何指针类型转为其他指针类型,是安全的。
- c++智能指针:
- unique_ptr持有对对象的独有权,同一时刻只能有一个unique_ptr指向给定对象。离开作用域时,若其指向对象,则将其所指对象销毁。
- shared_ptr允许多个该智能指针共享第“拥有”同一堆分配对象的内存,这通过引用计数(reference counting)实现,会记录有多少个shared_ptr共同指向一个对象,一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。
- 基于范围的for循环不能修改容器的内容,但是可以通过增加引用参数实现。
for(auto & x: books) ...
- override指出要覆盖一个虚函数,若父类没有改虚函数,则会报错。final则是禁止类被继承或者函数被重写。
- lambda以及包装器function:(p818)
- 可变参数模板:(p827)
- memset函数给整型数组赋值时只能赋值0,因为memset是内存操作,是设置每一个字节的值,所以赋值是不可取的。