目录
- 各种构造函数的调用规则
- 对象以值的方式给函数参数
- 用一个已有的对象去初始化另一个对象
- 函数的局部对象以值的方式从函数返回
- 调用规则1
- 调用规则2
- 多个对象的构造和析构
- 初始化列表
- 结束语
各种构造函数的调用规则
对象以值的方式给函数参数
实例:
class Maker
{
public:
Maker()
{
cout << "无参构造函数" << endl;
}
Maker(int a)
{
cout << "有参构造函数" << endl;
}
Maker(const Maker &maker)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
void func(Maker m)
{
}
void test01()
{
Maker m1;
func(m1);
}
对上述代码进行分析:
Maker m1;
实例化对象,并且该对象名为m1,在实例化对象过程中会调用Maker类中的无参构造。
func(m1);
该func函数中的形参为Maker m,并且传入该函数的形参为刚刚实例化完成的对象m1,所以实际上为Maker m = m1。即会调用Maker类中的拷贝构造函数。
最后在完成该函数生命周期时,会相反顺序依次调用析构函数来完成释放。
代码运行结果如下:
用一个已有的对象去初始化另一个对象
实例:
void test02()
{
Maker m1;
Maker m2(m1);
}
对上述代码进行分析:
Maker m1;
实例化对象,并且该对象名为m1,在实例化对象过程中会调用Maker类中的无参构造。
Maker m2(m1);
相当于调用Maker类中的拷贝构造函数。
代码运行结果如下:
函数的局部对象以值的方式从函数返回
实例:
Maker func2()
{
//局部对象
Maker m;
cout << "局部对象的地址:" << &m << endl;
return m;
}
void test03()
{
Maker m1 = func2();
cout << "m1对象的地址:" << &m1 << endl;
}
对上述代码进行分析:
func2函数的主要作用为将实例化成功的对象m的地址打印出来,并且将该对象作为该函数的返回值。
Maker m1 = func2();
将func2()的函数的返回值传递给m1,并且打印m1的地址。理论上m1和m的地址是一样的。
代码运行结果如下:
调用规则1
如果程序员提供了有参构造,那么编译器不会提供默认构造函数,但是会提供默认的拷贝构造
实例:
class Maker
{
public:
Maker()
{
cout << "无参构造函数" << endl;
}
Maker(int a)
{
cout << "有参构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
void test01()
{
Maker m(10);
Maker m2(m);
}
对上述代码进行分析:
Maker m(10);
调用Maker类中的有参构造函数,即a = 10
Maker m2(m);
使用该函数将会调用拷贝构造函数,但是我们发现在Maker类中并没有创建拷贝构造函数,我们先试试看程序能不能正常运行?
代码运行结果如下:
由上述可知,代码可以正常运行,并没有报错,这是因为编译器会提供默认的拷贝构造函数,只不过这个拷贝构造函数不做任何处理。
调用规则2
如果程序员提供了拷贝构造函数,那么编译器不会提供默认的构造函数和默认的拷贝构造函数
实例:
class Maker
{
public:
Maker(const Maker& maker)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
void test02()
{
Maker m;
}
对上述代码进行分析:
Maker m;
实例化对象m时,会自动调用构造函数,但是我们发现在Maker类中创建了拷贝构造函数,我们先试试看代码能不能正常运行?
代码运行结果如下:
从上可知,代码并不能正常运行,即我们可以得知,当提供了拷贝构造函数,那么编译器不会提供默认的构造函数和默认的拷贝构造函数。
多个对象的构造和析构
实例:
class BMW
{
public:
BMW()
{
cout << "BMW构造" << endl;
}
~BMW()
{
cout << "BMW析构" << endl;
}
};
class Buick
{
public:
Buick()
{
cout << "Buick构造" << endl;
}
~Buick()
{
cout << "Buick析构" << endl;
}
};
class Maker
{
public:
Maker()
{
cout << "Maker构造" << endl;
}
~Maker()
{
cout << "Maker析构" << endl;
}
private:
Buick bui;//成员对象
BMW bmw;//成员对象
};
void test01()
{
Maker m;
}
对上述代码进行分析:
private:
Buick bui;
BMW bmw;
在之前的实例中,我们一般私有化的时变量,并没有对对象进行私有化,但是实际上在C++中这个是可以私有化的,并且我们将该对象称为成员对象。
接下来我们先看看代码效果是什么样子的?
如上所示,首先会调用Buick构造函数,然后紧接着是BMW构造函数,最后才是Maker构造函数,细心的我们会发现如果类有成员对象,那么先调用成员对象的构造函数,再调用本身的构造函数,析构函数的调用顺序反之。
那么此时我们如果将Buick和BMW的顺序互换一下,代码运行效果会是什么呢?
代码运行结果如下:
并且还需要注意一点:
如果有成员对象,那么实例化对象时,必须保证成员对象的构造和析构能被调用
初始化列表
实例:
class BMW2
{
public:
BMW2(int a)
{
cout << "BMW2有参构造" << a << endl;
}
~BMW2()
{
cout << "BMW2析构" << endl;
}
};
class Buick2
{
public:
Buick2()
{
cout << "Buick2构造" << endl;
}
~Buick2()
{
cout << "Buick2析构" << endl;
}
};
class Maker2
{
public:
Maker2() :bmw(10)
{
cout << "Maker2构造" << endl;
}
~Maker2()
{
cout << "Maker2析构" << endl;
}
private:
Buick2 bui;//成员对象
BMW2 bmw;//成员对象
};
void test02()
{
Maker2 m;
}
int main()
{
test02();
}
对上述代码进行分析:
Maker2() :bmw(10)
{
cout << "Maker2构造" << endl;
}
当调用Maker2的构造函数时,会调用成员对象bmw的有参构造函数,并将10赋值给a。
代码运行结果如下:
那此时有多个对象需要指定调用某个构造函数,我们需要用逗号隔开。
实例如下:
class BMW2
{
public:
BMW2(int a)
{
cout << "BMW2有参构造" << a << endl;
}
~BMW2()
{
cout << "BMW2析构" << endl;
}
};
class Buick2
{
public:
Buick2(int b, int c)
{
cout << "Buick2构造" << endl;
}
~Buick2()
{
cout << "Buick2析构" << endl;
}
};
class Maker2
{
public:
Maker2(int a, int b, int c) :bmw(a), bui(b, c)
{
cout << "Maker2构造" << endl;
}
~Maker2()
{
cout << "Maker2析构" << endl;
}
private:
Buick2 bui;//成员对象
BMW2 bmw;//成员对象
};
void test02()
{
Maker2 m(30, 10, 20);
}
int main()
{
test02();
}
对上述代码进行分析:
Maker2(int a, int b, int c) :bmw(a), bui(b, c)
{
cout << "Maker2构造" << endl;
}
当使用Maker2进行实例化对象为m时,需要使用有参构造函数,将30,10,20这三个值分别传给bmw和bui对象,并且中间用逗号隔开。
代码运行结果如下:
除了以上几点,还有几点需要我们注意:
- 初始化列表是干什么用的,指定调用成员对象的某个构造函数
- 初始化列表只能写在构造函数
- 如果使用了初始化列表,那么所有的构造函数都要写初始化列表
- 可以使用对象的构造函数传递数值给成员对象的变量
结束语
如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!