c++学习笔记

复习考研中,记录c++一些知识点,若有错误之处请大家指出。

  • case后跟的只能是整型、字符型或枚举常量。
  • 类成员的作用域标识符(public,protected,private)是针对类的,类的成员函数可以访问类的私有属性,所以类的拷贝构造函数可以访问同类不同对象的私有属性。
  • 拷贝构造函数传入的参数必须是引用形式,因为如果是按值传递一个对象,会默认调用类的复制构造函数生成对象的一个副本,此时会造成无限递归错误。
  • 拷贝构造函数中的数据成员如果不用new开辟一个新的对象地址进行复制,那么两个对象会指向同一块内存。
  • 函数若返回被调用函数中声明的变量的引用,该变量需声明为static,否则函数执行结束后会销毁自动变量,造成返回值虚悬引用。
  • c++默认不允许在类定义中进行成员属性的初始化包括调用成员函数,但c++11后已经可以在类定义中定义成员属性的时候赋值了。
  • 不接受实参的构造函数,成为默认构造函数。
  • 如果用户显示提供了构造函数,编译器就不会提供一个无参的默认构造函数,需要程序员手动定义。
  • 判断函数重载是根据函数签名,函数签名是函数原型的函数名和形参类型(按顺序)部分。调用函数时实参类型会强制转换成形参指定的类型,任何单参数构造函数都可以被编译器执行隐式转换,可以用explicit避免。所以不建议形参数量相同但类型不同进行重载。另外,main函数不能被重载。
  • 构造函数调用顺序:继承的类(按顺序),子对象,自己。
  • 除非是对常量的引用,否则引用形参必须是左值。
  • 建议使用::(一元作用于分辨符)来引用全局变量。
  • 数组初始化列表元素少于数组元素个数,剩下的置零;若多于数组元素个数,是一个编译错误。
  • 数组名是一个常量指针,指向数组在内存中的起始地址。
  • 指针可以被初始化为0、NULL或相应类型的地址。
  • 将指针传递给函数有四种方式:指向非常量数据的非常量指针(int* a),指向常量数据的非常量指针(const int* a),指向非常量数据的常量指针(int* const a),指向常量数据的常量指针(const int* const a)。
  • sizeof(array)返回一个数组所占字节数,但如果array是一个函数的形参,比如size_t getsize(int* array),在这个函数里调用sizeof(array),只会返回该指针的字节数,即数组类型的字节数。sizeof只有在应用于一个类型名时才需要圆括号。
  • 对一个对象进行sizeof运算只报告该类的数据成员的大小,因为对象只包含数据。
  • void* 指针不能被间接引用,因为编译器无法确定所指向的明确字节数和数据类型。其他类型指针可以赋给void*指针,相反则不行。指针间的转换通常用reinterpret_cast(),有一篇博文详细说了各种转换。
  • 指针可以用相等和关系运算符进行比较,但只有在指针指向同一数组元素时比较才有意义。
  • 预处理器封套可以避免重复定义。
    #ifndef A_H
    #define A_H
    /*程序体*/
    #endif
  • 如果类的成员函数改变了private数据成员的值,且函数返回的是一个引用,那么在外部就可以改变该private成员,破坏封装和信息隐藏。
  • 按常量引用传递对象是一个既安全又高性能的方案。
  • const对象只能访问类的const成员函数。static成员函数只能访问static数据成员。
  • 成员初始化器在构造函数前执行,常量数据成员和声明为引用的数据成员必须用成员初始化器进行初始化。
  • 成员对象也最好通过成员初始化器进行性初始化,可以避免“双重初始化”成员对象的开销,一次是成员对象的默认构造函数被调用时,另一次是构造函数体中或后来调用设置函数被初始化成员对象时。
  • 本类的成员对象只能在本类进行初始化(成员初始化器),不能在派生类进行初始化。
  • 编译器会提供默认的复制构造函数,程序员也可以自定义复制构造函数。
  • 友元关系是授权的,不传递,也不是双向的。
  • this指针不是对象本身的一部分,作为一个隐式参数被编译器传递给对象的每个非static成员函数(因为static独立于类的对象而存在,而this指针必须指向具体对象)。它的类型取决于对象的类型及使用this的成员函数是否被声明为const。
  • 使用this指针可以进行串联函数调用。见博文。
  • static数据成员只可被初始化一次,不能在.cpp文件里将他们声明为static。static成员可以用类名::成员名直接访问。
  • 重载不改变运算符的优先级、结合律和元数,且不能创建新的运算符。
  • .,.*,?:,::四个运算符不能被重载。(),=,[],->必须被重载为成员函数。
  • 运算符作为成员函数重载时,左值一定是该类的对象(或引用),否则应该作为全局函数(最好是友元函数)重载。
  • 重载的后置自增运算符(a++)比前置自增运算符(++a)的原型参数多一个int作为标记,且该int没有实际作用。前置返回对象的引用,后置按值返回,所有后置不能作为左值使用。
    d1++;
    //成员函数调用d1.operator++(0);
    //全局函数调用operator++(d1,0);原型int operator++(int)
    ++d1;
    //成员函数调用d1.operator++();
    //全局函数调用operator++(d1);原型int operator++(int&, int)
  • 在一个类中声明另一个类的指针数据成员,可以使该类成为代理类,更好的实现封装。
    class Implementation
    {
        ...
    };
    
    /*代理类声明*/
    class Implementation;
    class Interface//代理类
    {
        ...
        Implementation* ptr;
    };
  • 派生类不会继承基类的构造函数、析构函数和赋值运算符,但可以调用。
  • 多态性利用了基类的指针句柄和引用句柄。
  • 派生类的对象可以当成基类对象进行处理,反之不能。
  • 派生类指针不能指向基类对象,反之可以,但利用基类指针,编译器只允许调用基类的成员函数。但可以通过向下强制转换将基类指针转换为派生类指针,具体见博文。
  • 利用virtual函数,调用的函数就由句柄所指向的对象类型决定,而非句柄类型,从而实现多态,消除不必要的switch逻辑。
  • 在执行时(而不是编译)选择合适的调用函数成为动态绑定,比如多态的实现。编译时确定调用承为静态绑定。
  • virtual 类型名 函数名(参数列表) = 0, 类似这样的函数是纯虚函数,有一个或多个纯虚函数的类是抽象类,不能实例化对象。
  • 有虚函数的类必须要有虚析构函数。
  • 在析构函数中调用虚函数只会静态联编,不会动态联编进行多态调用。
  • 函数模板和宏一样,都能实现软件复用,不同的是函数模板通过完整的c++类型检测消除了许多类型错误。
  • 函数模板尽管有复用的有点,但并不能节省空间,实际上复用时产生的模板特化的副本和程序员手动重载版本的长度相等。
  • 函数匹配优先级:完全匹配普通函数>特化模板>模板
  • 若一个类模板中有一个静态成员,那么每一个类模板特化得到一份属于他们自己的类模板静态成员的副本。而一个类模板特化实例化的所有对象共享一个静态成员。
  • c++使用类型安全的I/O,任何一次I/O操作都是对数据类型敏感的。
  • cerr和clog都是连接到标准错误设备,区别在于cerr无缓冲而clog是有缓冲的。
  • c++能自动判定数据类型,但假设想打印一个表示字符串的char*值(即第一个字符的内存地址),<<运算符已被重载为打印一个以空字符结尾的字符串。此时需要static_cast<void*>(word)来输出地址。其他指针变量同理。
  • 异常处理是设计用来处理同步错误的,并不处理相关异步事件。
  • 所有异常都直接或间接从拥有virtual函数what的exception类派生而来。
  • 一个catch只有一个参数,且一个try语句块后不能有两个参数类型相同的catch。
  • 抛出的类型和异常参数类型相同或者是它的继承类都可以匹配。
  • 抛出表达式值时一定要小心类型的转换,可能导致和异常参数类型不符。
  • 应避免利用异常处理作为控制流的替代模式。
  • 使用auto_ptr可以防止异常发生在成功分配内存和delete执行之间导致内存泄漏的情况。但auto_ptr不能用于标准容器库因为auto_ptr对象在复制时传送内存所有权。
  • 捕获基类对象的catch处理器应当放在捕获派生类对象的catch后面,否则是一个逻辑错误。
  • c++没有在文件上强加任何结构。将每个文件都看作字节数据流。
  • ios::app输出完会自动后移,而ios:ate不会,它总在当前最后位置,但可以在任意位置写数据。若ios::out打开一个已存在文件,会被擦出后重新创建。
  • 随机存取文件(write,read)要注意将对象转换成const char*类型。
  • 结构体成员并不一定存储于内存的连续字节中,比如若计算机要求成员以字对齐,那么char类型成员在内存空间后会存在一字节的“洞”。所以结构体成员值相等不代表两个结构体相等。
  • c++可以对int或枚举类型设置位域,未命名的位域可以用来对域进行填充使得下一位域向新的存储单元对齐(0)表示跳过该存储单元剩余位数。
  • c风格字符数组操作函数中,strcpy需要手动添加'\0'。
  • strtok在记号化字符数组的过程中会用'\0'替换分隔符。
  • vector可以预先分配一个空间,且当内存空间耗尽时,它会分配一个更大的连续空间,把原先的数据复制到新的空间,并释放原空间。但可能造成空间的浪费。使用resize可以更好控制空间的使用。
posted @ 2019-03-19 21:30  没有窗户的小巷  阅读(200)  评论(0编辑  收藏  举报