C++ 中的构造函数

一、balabala

C++的特点是面向对象,创建一个对象需要调用构造函数。构造函数的本意是在创建对象的时候初始化对象,编译器会根据传递的实参来匹配不同的(重载的)构造函数。而根据构造函数发挥的功能不同,构造函数可以被区分为:默认构造函数,普通构造函数,拷贝构造函数,转换构造函数。下表简单地列出来这些不同类型地构造函数所发挥的功能:

函数类型 功能
默认构造函数 编译器自动生成的构造函数
普通构造函数 用户自定义的构造函数
拷贝构造函数 在以拷贝的方式初始化对象
转换构造函数 将其它类型转换为当前类类型对象

乍一看,这么多构造函数,还挺难的。实际上,就是利用函数的多态性(关于构造函数主要是传参不同)来重载构造函数。笔者认为,其实没必要分的这么细,正如上面所说,构造函数的本意就是在创建对象的时候初始化对象,编译器会根据你传入参数的不同来调用不同的构造函数(这点看起来是相当智能的)。本质上就是调用了一个构造函数来初始化了一个对象,把构造函数分的这么细,应该是为了描述方便,用“构造函数1、构造函数2、构造函数3…”远没有“默认构造函数、拷贝构造函数、转换构造函数…”听起来那么动听。。但是起这么多名字,让像我这种小白看起来还怪害怕的,实际上,理解了,就不难了。这篇博客就是为了给新手村小白整个攻略,顺便加深一下自己的理解。

比如,我们现在有个Complex复数类,你想创建一个复数对象时,如果你用Complex();就是调用了默认构造函数,因为没有参数;如果你用Complex(1,2)就是调用了普通构造函数;如果你用Complex(c1)就是调用了拷贝构造函数,因为传入参数是一个复数类型的c1;如果你用Complex(1.0)就是调用了转换构造函数。

当然,在没有定义这些构造函数之前,你是不能这么随意地创建这些复数对象的。下面会有一个关于复数类的例子。

二、example

#include <iostream>
using namespace std;

//复数类
class Complex {
    
    
public:
	Complex() : m_real(0.0), m_imag(0.0) {
    
     }                            //默认构造函数
	Complex(double real, double imag) : m_real(real), m_imag(imag) {
    
     }  //普通构造函数
	Complex(double real) : m_real(real), m_imag(0.0) {
    
     }                //转换构造函数
	Complex(const Complex &c) : m_real(c.m_real),m_imag(c.m_imag) {
    
     }   //拷贝构造函数
public:
	friend ostream & operator<<(ostream &out, Complex &c);  //友元函数
	double get_real() {
    
     return m_real; }
	double get_imag() {
    
     return m_imag; }
private:
	double m_real;  //实部
	double m_imag;  //虚部
};

//重载>>运算符
ostream & operator<<(ostream &out, Complex &c) {
    
    
	out << c.m_real << " + " << c.m_imag << "i";;
	return out;
}

int main() {
    
    
	Complex a;
	cout << "默认构造函数 Complex():" << endl;
	cout << a << endl;

	cout << "普通构造函数 Complex(10.0, 20.0):" << endl;
	a=Complex(10.0, 20.0);cout << a << endl;

	cout << "转换构造函数 Complex(25.5):" << endl;
	a = 25.5;  //调用转换构造函数
	cout << a << endl;

	cout << "拷贝构造函数 Complex(a):" << endl;
	Complex b = a;
	cout << b << endl;

	cin.get();
	return 0;
}

输出结果如下:

默认构造函数 Complex()0 + 0i
普通构造函数 Complex(10.0, 20.0)10 + 20i
转换构造函数 Complex(25.5)25.5 + 0i
拷贝构造函数 Complex(a)25.5 + 0i

实际上,如果上面的代码中没有默认构造函数和拷贝构造函数的定义,也可以运行成功,因为编译器会自动加入默认构造函数和拷贝构造函数的代码。值得一提的是,如果类带有指针变量,并有动态内存分配,则必须人工定义一个拷贝构造函数。

除了在创建对象时初始化对象,其他情况下也会调用构造函数,例如,以拷贝的的方式初始化对象时会调用拷贝构造函数(上面的b=a),将其它类型转换为当前类类型时会调用转换构造函数(上面的a=25.5)。这些在其他情况下调用的构造函数,就成了特殊的构造函数了。特殊的构造函数并不一定能体现出构造函数的本意。实际上,所谓”特殊的构造函数“只是隐式地调用了构造函数,比如Complex(10.0, 20.0)是直接调用了普通构造函数,而a = 25.5;等同于a=Complex(25.5),隐式地调用了转换构造函数。

三、拷贝构造函数

关于拷贝构造函数,有几点需要注意的地方,如下:

1、调用拷贝构造函数的几个步骤:

  • 比如有个test对象传入形参,会先产生一个临时变量,就叫C吧。
  • 然后调用拷贝构造函数把test的值给C。
  • 等函数执行完之后,再析构掉C对象。

2、拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用拷贝构造函数,当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用拷贝构造函数,也就是说:

A x(2);  //直接初始化,调用构造函数
A y = x; //拷贝初始化,调用拷贝构造函数

3、必须定义拷贝构造函数的情况:

  • 只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显示地定义拷贝构造函数也可以拷贝;
  • 有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这种情况下都必须定义拷贝构造函数。

4、什么情况下使用拷贝构造函数?

类的对象需要拷贝时,拷贝构造函数会被调用,以下情况都会调用拷贝构造函数:

  • 一个对象以值传递的方式传入函数体
  • 一个对象以值传递的方式从函数返回
  • 一个对象需要通过另外一个对象进行初始化

参考

C语言中文网
菜鸟教程

猜你喜欢

转载自blog.csdn.net/Gou_Hailong/article/details/112887561