C++中的五种构造函数
C++中的构造函数可以分为5类:默认构造函数、普通构造函数、拷贝构造函数、转换构造函数、移动构造函数。
默认构造函数
未提供显式初始值时,用来创建对象的构造函数。以Student类为例,默认构造函数的原型为
Student();//没有参数
Student(int num=0;int age=0);//所有参数均有默认值
何时生成默认的构造函数
当一个类没有构造函数时,如果满足以下四个条件其中之一,则编译器会为该类自动生成一个默认的构造函数:
- 该类含有一个类类型(非内置类型)的成员变量,且该类型含有默认构造函数。
- 该类继承自含有默认构造函数的基类。
- 该类继承或声明了虚函数。
- 该类含有虚基类。
普通构造函数
C++用于构建类的新对象时需要调用的函数
Student(int num,int age);//有参数
拷贝构造函数
何时生成默认的拷贝构造函数
当一个类没有拷贝构造函数时,如果满足以下四个条件其中之一,则编译器会为该类自动生成一个默认的拷贝构造函数:
- 该类含有一个类类型(非内置类型)的成员变量,且该类型含有拷贝构造函数。
- 该类继承自含有拷贝构造函数的基类。
- 该类继承或声明了虚函数。
- 该类含有虚基类。
需要注意的是,默认的拷贝构造函数实现的是浅拷贝。
拷贝构造函数调用的时机
拷贝构造函数在以下三种情况下会被调用。
1).当用一个对象去初始化同类的另一个对象时,会引发复制构造函数被调用。例如,下面的两条语句都会引发复制构造函数的调用,用以初始化 c2。
Complex c2(c1);
Complex c2 = c1;
这两条语句是等价的。
注意,第二条语句是初始化语句,不是赋值语句。赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发复制构造函数的调用。例如:
Complex c1, c2;
c1=c2;
这条语句不会引发复制构造函数的调用,因为 c1 早已生成,已经初始化过了。
2).如果函数 F 的参数是类 A 的对象,那么当 F 被调用时,类 A 的复制构造函数将被调用。换句话说,作为形参的类A的对象,是用复制构造函数初始化的,而且调用复制构造函数时的参数,就是调用函数时所给的实参。
#include<iostream>
using namespace std;
class A{
public:
A(){};
A(A & a){
cout<<"Copy constructor called"<<endl;
}
};
void Func(A a){ }
int main(){
A a;
Func(a);
return 0;
}
程序的输出结果为:
Copy constructor called
这是因为 Func 函数的形参 a 在初始化时调用了复制构造函数。
前面说过,函数的形参的值等于函数调用时对应的实参,现在可以知道这不一定是正确的。如果形参是一个对象,那么形参的值是否等于实参,取决于该对象所属的类的复制构造函数是如何实现的。例如上面的例子,Func 函数的形参 a 的值在进入函数时是随机的,未必等于实参,因为复制构造函数没有做复制的工作。
以对象作为函数的形参,在函数被调用时,生成的形参要用复制构造函数初始化,这会带来时间上的开销。如果用对象的引用而不是对象作为形参,就没有这个问题了。但是以引用作为形参有一定的风险,因为这种情况下如果形参的值发生改变,实参的值也会跟着改变。
如果要确保实参的值不会改变,又希望避免复制构造函数带来的开销,解决办法就是将形参声明为对象的 const 引用。例如:
void Function(const Complex & c)
{
...
}
3). 如果函数的返冋值是类 A 的对象,则函数返冋时,类 A 的复制构造函数被调用。换言之,作为函数返回值的对象是用复制构造函数初始化 的,而调用复制构造函数时的实参,就是 return 语句所返回的对象。例如下面的程序:
#include<iostream>
using namespace std;
class A {
public:
int v;
A(int n) { v = n; };
A(const A & a) {
v = a.v;
cout << "Copy constructor called" << endl;
}
};
A Func() {
A a(4);
return a;
}
int main() {
cout << Func().v << endl;
return 0;
}
程序的输出结果是:
Copy constructor called
4
转换构造函数
一个构造函数接收一个不同于其类类型的形参,可以视为将其形参转换成类的一个对象。像这样的构造函数称为转换构造函数。在 C++ string 类中可以找到使用转换构造函数的实用示例。string 类提供一个将 C 字符串转换为 string 的转换构造函数
class string
{
//仅显示转换构造函数
public:
string(char *);//形参时其他类型变量,且只有一个形参
};
移动构造函数
所谓移动语义,指的就是以移动而非深拷贝的方式初始化含有指针成员的类对象。简单的理解,移动语义指的就是将其他对象(通常是临时对象)拥有的内存资源“移为已用”。事实上,对于程序执行过程中产生的临时对象,往往只用于传递数据(没有其它的用处),并且会很快会被销毁。因此在使用临时对象初始化新对象时,我们可以将其包含的指针成员指向的内存资源直接移给新对象所有,无需再新拷贝一份,这大大提高了初始化的执行效率。
#include <cstring>
using namespace std;
class Str{
public:
char *str;
Str(Str &&s)//移动构造函数
{
}
~Str()
{
}
};
更多推荐
所有评论(0)