C++ 详解构造函数,默认构造,拷贝构造,自定义构造

如何禁止构造函数的使用,什么是类的默认构造函数?构造函数、析构函数和虚函数,拷贝构造函数,一篇解决

如何禁止构造函数的使用

可以为类的构造函数增加delete修饰符,虽然晟敏改了构造函数但是禁止使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

class A {
public:
int var1, var2;
A(){
var1 = 10;
var2 = 20;
}
A(int tmp1, int tmp2) = delete;
};

int main()
{
A ex1;
A ex2(12,13); // error: use of deleted function 'A::A(int, int)'
return 0;
}

什么是类的默认构造函数?

默认构造函数不提供任何实参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

using namespace std;

class A
{
public:
A(){ // 类的默认构造函数
var = 10;
c = 'q';
}
int var;
char c;
};

int main()
{
A ex;
cout << ex.c << endl << ex.var << endl;
return 0;
}
/*
运行结果:
q
10
*/

构造函数、析构函数和虚函数

构造函数一般不定义为虚函数。 构造函数是在实例化对象的时候进行调用,如果此时将构造函数定义为虚函数,需要访问对象内存空间中的虚表指针,指向虚函数表找到对应的虚函数。虚函数表是与类绑定的,所以编译的时候就有,但是只有创建了对象才有虚函数指针,但是得通过构造函数创建对象,对象还没有创建,所以也无法进行虚函数的调用。

析构函数一般要定义为虚函数。 析构函数是要对象进行释放的,如果类之间由继承的关系,用基类指针指向了派生类的对象,但没有定义成虚函数,那么就不能调用对象各自的析构函数,就会造成内存泄漏。

拷贝构造函数

复制构造函数是构造函数的一种,也称拷贝构造函数,它只有一个参数,参数类型是本类的引用。

如果类的构造者自己不写构造函数的话,编译器也会自动生成一个拷贝构造函数,所以拷贝构造函数是一直存在的。如果自己写构造函数的话,那么就会覆盖掉默认的拷贝构造函数;

拷贝构造函数的一般定义:

1
classname (const classname &obj);

所以说本质就是对于类的对象的引用,通过已有的对象来初始化一个新的对象

1
A (const A &obj);

使用拷贝构造函数初始化一个新的对象:

1
2
A c1(1,2); // 使用自己写的构造函数初始化了一个对象c1
A c2(c1); // 使用已经初始化好的对象,通过拷贝构造函数创建了一个新的对象

使用自定义的拷贝构造函数初始化一个对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
class Complex{
public:
double real, imag;
Complex(double r,double i){
real = r; imag = i;
}
Complex(const Complex & c){ // 自己定义的拷贝
real = c.real; imag = c.imag;
cout<<"Copy Constructor called"<<endl ;
}
};

int main(){
Complex cl(1, 2);
Complex c2 (cl); //调用复制构造函数
cout<<c2.real<<","<<c2.imag;
return 0;
}
1
2
3
Complex c2(c1);
Complex c2 = c1;//这两条语句是等价的

禁用拷贝构造函数/避免拷贝

在c++11之后,出现了delete弃置函数关键帧字,就像之前我们可以直接用delete不用构造函数一样。

1
2
3
4
5
6
7
8
9
10
11
12
class noncopyable {
protected:
noncopyable() = default;
~noncopyable() = default;
public:
noncopyable(const noncopyable&) = delete; //拷贝构造函数
noncopyable& operator=(const noncopyable&) = delete; // 赋值构造函数
};

class foo : private noncopyable {
};

使用operator关键字将c++中原本的运算符进行重载。

使用的基本方式:

如果运算符重载是类的成员函数:(参数列表里写一个参数就行了)

1
2
{返回类型} operator {运算符} {参数列表}
Box operator + (const Box&)

如果运算符重载不是类的成员函数:(参数列表里两个都要写出来)

1
Box operator + (const Box&,const Box&)

通过运算符重载,我们可以定义赋值构造函数:

1
2
3
4
5
返回类型:noncopyable&
关键字:operator
运算符:=
参数:const noncopyable&
noncopyable& operator=(const noncopyable&)

如何减少构造函数的开销

在构造函数中使用类初始化列表会较少调用默认的构造函数产生的开销

1
2
A (): ex(1) {
}

不在函数体内对变量赋值,而是在参数列表后,跟一个冒号和初始化列表,效率会更好。

对于类内部非内置类型成员,初始化列表:只调用一次构造;在函数体内赋值:一次默认构造+一次拷贝赋值。非内置类型如string,容器等。