cpp_self_study
查缺补漏,重点整理
引用
这里先只谈左值引用。引用和被引用变量是一回事,个人认为实质上是二者所代表的变量地址相同。
函数返回值是引用
例如:
1 |
|
可以将函数调用作为赋值对象。
常引用
指的是
const int &
,不能通过常引用去修改其引用的变量。
不能将常引用初始化给非常引用:
1 | const int &p = n; |
函数重载
这里重载的定义是:函数名相同,函数参数个数或参数类型不同。
因此,函数参数列表相同但返回值类型不同,是不允许重载的。
如果在类里,还可以通过分别定义非常量成员函数和常量成员函数(函数定义后面加
const
)来重载。
例:
1 | int fuck(int n){return n;} |
函数缺省参数
指的是可以给函数参数列表后面几个连续的参数默认值。
1 | int fuck(int n, int m = 0){return n + m;} |
这里主要理解缺省参数存在的意义,它主要是为了优化程序的可扩充性。在初步进行需求开发时,可能对于一个接口的功能并没有完善的规划。到后期有了更完善的需求,可能会在同一个接口上扩充功能。这时可以在该接口(这里指函数)后面添加缺省参数,通过这个缺省参数来在扩充功能的同时,保证原程序中无需扩充功能的函数调用不需要修改调用形式(即一个一个在函数尾部添加参数)。
然后提醒一下,对于重载+缺省产生的二义性:
1 | int fuck(int n = 1){return n * 2;} |
当使用 fuck()
调用时,产生的二义性会让编译器产生错误。
类
私有成员
这里主要提醒一件事情,就是对于 private
修饰的成员,只能在成员函数内部访问。但是成员函数内部可以访问自己以及其它相同类的私有成员:
1 | class fuck{ |
复制构造函数
指的是形如
T (T &)
或T (const T &)
这样的用另一个对象的引用来初始化该对象的构造函数。如果不自己定义,会自动生成一个默认的复制构造函数。新增tip:对于封闭类,编译器为其生成默认的复制构造函数时,会按照先调用其成员对象的复制构造函数的规则生成,而不是无参构造函数。
复制构造函数在三种情况下起作用:
- 用一个对象去初始化另一个对象:
Complex c2(c1);
,Complex c2 = c1; //初始化,不是赋值
- 一个函数中有一个参数是类A的对象,调用该函数时,类A的复制构造函数会被调用。
- 函数的返回值是类A的对象,则函数返回时会被调用,将返回值赋值给临时对象(注意)。
当然,像g++这种编译器可能会进行优化,可能不会生成临时对象,就少了中间的临时对象的复制构造函数和析构函数的调用(不愧是g++,主打一个激进)。而msvc这种就会按照C++的规定来编译。
类型转换构造函数
之前没听说过
指的是只有一个参数的构造函数。
这样无论是使用 =
进行赋值还是初始化,可以进行自动类型转换。具体看例子吧:
1 |
|
静态成员
对于类的静态变量,需要在定义类的文件中对其进行一次说明或初始化,否则会发生链接错误:
1 |
|
思考:如何维护这个total_number呢?应该在怎么样的构造函数和析构函数来实时维护该变量?
封闭类构造函数/析构函数
封闭类:含有成员对象的类
封闭类对象生成时,先执行所有对象成员的构造函数(按照类中的说明次序,与初始化列表顺序无关),然后执行封闭类的构造函数。
封闭类对象消亡时,先执行封闭类对象的析构函数,再执行成员对象的析构函数(按照构造函数调用的反序)。
重载自增自减运算符
Tip:C++约定俗成的规则,就是前置形式的 ++c
返回的是对象
c
的引用,c++
返回的是新的对象。
因此可以这么写 (++c)=1
。重载的时候注意返回值类型。
可以注意到前置的自增自减运算符效率更高,因为后置的情况会导致对象的拷贝。这也是为什么我喜欢在acm中写for循环喜欢写
for(int i = 0; i < n; ++i)
,当然对于内置整形变量其实无所谓了,更看个人风格。
protected
派生类可以访问的是当前对象的基类对象的protected成员,而不能访问非当前对象的protected成员。
多态
主要有两种表现方式:基类指针指向派生类对象、基类引用派生类对象
Tip:在类的成员函数(非构造、非析构)中调用虚函数,等价于
this
指针调用虚函数,表现为多态。而如果是构造函数和析构函数就不是多态(想想也是嘛,多态函数得等对象初始化完才能用)。
Tip2:派生类中和基类的虚函数同名同参数表的函数可以不加
virtual
。