前言:本笔记所对应的课程为中国大学mooc中北京大学的程序设计与算法(三)C++面向对象程序设计,主要供自己复习使用,且本笔记建立在会使用c和java的基础上,只针对与c和java的不同来写
第二章 类和对象基础
类和对象的基本概念补充
- 某成员缺省关键字时,默认为private
- 访问范围:在类的成员函数内部,能访问当前对象和其他同类对象的全部属性和函数;在类的成员函数以外的地方,只能访问该对象的public成员。
- 同时使用缺省参数和重载时要注意避免二义性。
构造函数
如何创建一个对象:
class Complex {
private:
int v;
public:
Complex() { v = 0;} //(1)
Complex(int n) { v = n;} //(2)
Complex(int n, int m) { v = n - m;} //(3)
};
Complex c1;
Complex * pc1 = new Complex;
Complex c2(2), c3(3);
Complex * pc2 = new Complex(2);
Complex array1[3]; //产生3个(1)初始化的对象
Complex array2[3] = {3, 0}; //产生两个(2)初始化的对象,产生一个(1)初始化的对象
Complex * array3 = new Complex[3]; //产生三个(1)初始化的对象
Complex array4[3] = {4, Complex(1, 2)}; //产生三个对象,分别用(2)(3)(1)初始化
Complex * array5 = {new Complex(4), NULL, new Complex(5, 6)}; //产生两个对象,分别用(2)(3)初始化
注意:构造函数在缺省时,会自动生成一个不带参数的构造函数;但当存在带参数的构造函数时,则不会生成不带参数的构造函数。
复制构造函数
用于将一个对象复制。形如X::X(X &)
或者X::X(const X &)
。如果没有明确定义,则会生成默认的一个复制构造函数用于复制对象;而当自己定义了复制构造函数时,默认的复制构造函数将不存在。
参数必须是引用。
复制构造函数起作用的三种情况:
- 用一个对象去初始化同类的另一个对象时。(即直接调用复制构造函数)有两种不同的写法,见下面的代码。
- 当一个函数的参数是某个类的对象时,那么调用这个函数,该类的复制构造函数会被调用。
- 当一个函数的返回值是某个类的对象时,那么调用这个函数,该类的复制构造函数会被调用。
对于第二种情况,由于复制对象会造成较大的系统开销,因此当想要避免这种情况的时候,可以将参数改为引用的形式;如果还不希望该对象被修改,那么还可以加上const
关键字。
类型转换构造函数
用于实现类型的自动转换。只有一个参数且不是复制构造函数的构造函数都称为类型转换构造函数。
class Complex {
private:
int v;
public:
Complex() { v = 0;}
Complex(int n) {
//只有此函数是类型转换构造函数,而其他两个构造函数则不是
v = n;
cout << "调用了类型转换构造函数" << endl;
}
Complex(int n, int m) { v = n - m;}
Complex(const Complex & c){
v = c.v;
cout << "调用了复制构造函数" << endl;
}
int getV() {return v;}
};
Complex c1(9);
Complex c2 = c1;
Complex c3(c1);
//以上两句话是直接调用复制构造函数的两种等价用法。注意第二句中的等号是初始化的意思,而不是赋值的意思。
c3 = c2;
//这句话中的等号是赋值的意思(由于c2c3都是已经存在的对象),所以既不调用复制构造函数,也不调用类型转换构造函数。
Complex c4(9, 3);
Complex c5 = 12;
//上面这句话是类型转换构造函数的用法。
//其原理是,检查到等号两侧类型不同,所以将12先调用类型转换构造函数转换成一个临时的Complex对象,然后再将其赋值给c5。
//注意和上面不同的是,这里赋值给c5时并没有调用复制构造函数,这里的等号的意义是赋值,而不是初始化。
c4 = 11;
//除了像上面那样用类型转换构造函数初始化一个对象外,还可以改变一个现有的对象。同样这里也并没有调用复制构造函数,等号的意义是赋值。
总结:
类型转换构造函数有两个功能,当显式调用时,用于初始化一个对象;当隐式调用时,用于强制类型转换。如果想禁用隐式调用的功能,则可以在函数前加关键字explicit
。此时,上面的两种用法Complex c5 = 12
和Complex c4 = 11
都无法通过编译,只能写成Complex c5 = Complex(12)
的形式。
析构函数
- 析构函数在对象消亡时被自动调用,可用来做释放空间等工作。
- 一个类只能有一个析构函数。写法:在无参数的构造函数前加一个
~
就是析构函数。 - 没有显式定义析构函数的时候,生成缺省析构函数,缺省析构函数什么都不做。
- 对象作为函数参数或函数返回值时析构函数都很可能会被调用。
- main函数结束的时候会自动回收对象,因此会调用析构函数。
- new出的对象如果没有用delete删除,在main函数结束的时候不会被自动回收,因此不会调用析构函数。