构造函数
- 成员函数的一种
- 名字与类名相同,可以有参数,不能有返回值(void也不行)
- 作用是对对象进行初始化,如给成员变量赋初值
- 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数
- 一个类可以有多个构造函数
构造函数调用方式:
- 显式调用:CLASS a=CLASS(参数列表);
- 隐式调用:CLASS a(参数列表);
为什么需要构造函数:
- 构造函数执行必要的初始化工作,有了构造函数,就不
必专门再写初始化函数,也不用担心忘记调用初始化函数。 - 有时对象没被初始化就使用,会导致程序出错。
几种特殊构造函数:
默认构造函数:
- 即无参数(或者缺省类型参数)的构造函数
- 如果定义类时没写构造函数,则编译器生成一个默认的无参数 的构造函数
- 默认构造函数无参数,不做任何操作
- 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
类型转换构造函数:
- 通常来说只有一个参数且不是复制构造函数的构造函数即可认为是类型转换函数。
也就是说只有一个参数的构造函数允许使用赋值语法来初始化对象和进行赋值操作。 - 需要注意的是,使用类型转换构造函数初始化时不会生成临时对象,而进行赋值操作时编译系统会自动调用转换构造函数,建立 一个无名的临时对象(或临时变量)。
- 但有时候设计者可能希望单参数构造函数有其他作用,此时为了防止语义混淆不清,可以使用explicit关键字防止类构造函数的隐式自动转换
.例子如下:
class Complex {
public:
double real, imag;
explicit Complex( int i) {//显式类型转换构造函数
cout << "IntConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r,double i) {real = r; imag = i; }
};
int main () {
Complex c1(7,8);
Complex c2 = Complex(12);
c1 = 9; // error, 9不能被自动转换成一个临时Complex对象
c1 = Complex(9) //ok
cout << c1.real << "," << c1.imag << endl;
return 0;
}
- 类型转换构造函数将double ,int等其他类型值转换为对象。那么对应就有转换函数可以将对象转换为其他数据类型。
格式如下:在class内:operator typename()。同样的也可以用关键字explicit来关闭其隐式转换(c++11)
复制(拷贝)构造函数:
- 即只有一个参数且为对象的引用的构造函数(A::A(A & )或A::A(const A&),A为某构造类)。
- 如果没有定义复制构造函数,那么编译器生成默认 复制构造函数。默认的复制构造函数完成复制功能。
- 如果定义的自己的复制构造函数, 则默认的复制构造函数不存在。
复制构造函数起作用的三种情况
- 当用一个对象去初始化同类的另一个对象时。
Complex c2(c1);
Complex c2 = c1; //初始化语句,非赋值语句
- 如果某函数有一个参数是类 A 的对象,那么该函数被调用时,类A的复制构造函数将被调用。
void Func(A a1){ }
int main(){
A a2;
Func(a2);
return 0;
}
程序输出结果为: Copy constructor called
- 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用:
A Func() {
A b(4);
return b;
}
int main() {
cout << Func().v << endl;
return 0;
}
输出结果:
Copy constructor called
※注意:对象间赋值并不导致复制构造函数被调用
默认复制构造函数可能出现的问题:
- 复制构造函数是按值进行复制的,当遇到类成员里有指针变量时,它无法复制指针指向的内容而仅仅是复制了地址,使得两指针指向同一片内存。
- 默认构造函数在其创建的临时对象消亡时会调用析构函数,当析构函数是设计者创建含静态变量的类时,如在顺序栈类中,析构函数显然需要将top–,如若不小心调用默认的复制构造函数创建了对象,那么对象消亡时调用析构函数会使top–即多减了1.
常量引用参数的使用
void fun(CMyclass obj_ ) {
cout << "fun" << endl;
}
这样的函数,调用时生成形参会引发复制构造函数调用,开销比较大。
所以可以考虑使用 CMyclass & 引用类型作为参数。
如果希望确保实参的值在函数中不应被改变,那么可以加上const 关键字:
void fun(const CMyclass & obj) {
//函数中任何试图改变 obj值的语句都将是变成非法
}
初始化列表
用于构造函数给成员变量赋值的一种简单写法。
析构函数:
-
名字与类名相同,在前面加‘ ~’ , 没有参数和返回值,
-
一个类最多只能有一个析构函数。
-
析构函数对象消亡时即自动被调用。
比如
delete对象
参数对象消亡
临时对象消亡 -
可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。
-
如果定义类时没写析构函数,则编译器生成缺省析构函数。 缺省析构函数什么也不做。
-
如果定义了析构函数,则编译器不生成缺省析构函数。
关于构造函数和析构函数什么时候被调用:
C++自动提供的成员函数:
- 默认构造函数
- 默认析构函数
- 复制构造函数
- 赋值运算符
- 地址运算符
有时候这些默认的定义会与设计的类需要的功能不符,所以需要自己设计默认构造函数,析构函数,复制构造函数以及重载相关的运算符。
(本文参考北大郭炜老师的慕课程序设计与算法三和《C++primer plus 第六版》,如有错误还望大佬指出)