(P14)构造函数与析构函数:拷贝构造函数
原创
©著作权归作者所有:来自51CTO博客作者喜欢打篮球的普通人的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
- 1.拷贝构造函数
- 2.拷贝构造函数调用的几种情况
1.拷贝构造函数
- 功能:使用一个已经存在的对象来初始化一个新的同一类型的对象
- 本质:就是用一个对象初始化另外一个对象,会调用拷贝构造函数
(1)可能是实参初始化形参时,若是值传递会调用拷贝构造函数
(2)函数返回的是一个对象时,也可能是值传递,也会调用拷贝构造函数
(3)Test t2=t;也会调用拷贝构造函数 - 声明:只有一个参数并且参数为该类对象的引用
- 如果类中没有说明拷贝构造函数,则系统自动生成一个缺省拷贝构造函数(复制构造函数),作为该类的公有成员
- eg:14cpp\14cpp\14cpp\01.cpp
#include "Test.h"
int main(void)
{
Test t(10);
//Test t2(t); // 用t对象初始化t2对象,调用拷贝构造函数
Test t2 = t; // 等价于Test t2(t);这里的=不是运算符,编译器对他会有特殊的解释
return 0;
}
- 类声明及定义
14cpp\14cpp\14cpp\Test.h
#ifndef _TEST_H_
#define _TEST_H_
class Test
{
public:
// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
// 默认的构造函数
Test();
explicit Test(int num);
//为啥不能是这样?Test(const Test other);
//实参t初始化同一种类型的对象other,若不用引用,则是值传递,实参初始化形参调用拷贝构造函数,调用
//到拷贝构造函数又涉及到实参初始化形参,会调用拷贝构造函数,所以这边应该调用拷贝构造函数
//引用没有自己独立的地址空间,它与实参共享同一个空间,所以不会再构造出一个对象出来了
//用引用传递可以减少内存的复制,对象的拷贝,可以提供效率
Test(const Test& other);//拷贝构造函数:所接受的参数是对象的引用
void Display();
Test& operator=(const Test& other);
~Test();
private:
int num_;
};
#endif // _TEST_H_
14cpp\14cpp\14cpp\Test.cpp
#include "Test.h"
#include <iostream>
using namespace std;
// 不带参数的构造函数称为默认构造函数
Test::Test() : num_(0)
{
//num_ = 0;
cout<<"Initializing Default"<<endl;
}
Test::Test(int num) : num_(num)
{
//num_ = num;
cout<<"Initializing "<<num_<<endl;
}
Test::Test(const Test& other) : num_(other.num_)
{
//num_ = other.num_;//函数体中初始化本质是赋值,不是初始化
cout<<"Initializing with other "<<num_<<endl;
}
Test::~Test()
{
cout<<"Destroy "<<num_<<endl;
}
void Test::Display()
{
cout<<"num="<<num_<<endl;
}
Test& Test::operator=(const Test& other)
{
cout<<"Test::operator="<<endl;
if (this == &other)
return *this;
num_ = other.num_;
return *this;
}
- 测试:
第一次调用带参数的构造函数;
第二次用t对象初始化t2对象,所以调用拷贝构造函数
2.拷贝构造函数调用的几种情况
- 当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。
- eg:14cpp\14cpp\14cpp\02.cpp
#include "Test.h"
#include <iostream>
using namespace std;
void TestFun(const Test t)//对象t的作用域在块作用域以内,当生存期结束,就会销毁
{
}
void TestFun2(const Test& t)
{
}
Test TestFun3(const Test& t)
{
return t;
}
const Test& TestFun4(const Test& t)
{
//return const_cast<Test&>(t);
return t;
}
int main(void)
{
Test t(10);
TestFun(t);//会调用拷贝构造函数
TestFun2(t);//不会调用拷贝构造函数,因为传递的是引用,这里形参不会构造一个对象,分配内存,形参和实参共享同一块内存
return 0;
}
- 类声明及定义如上
- 测试:
- 当函数的返回值是类对象,函数执行完成返回调用者时使用拷贝构造函数。
理由也是要建立一个临时对象中,再返回调用者。为什么不直接用要返回的局部对象呢?
因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。 - 所谓return 对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。
- eg:14cpp\14cpp\14cpp\03.cpp
#include "Test.h"
#include <iostream>
using namespace std;
void TestFun(const Test t)//对象t的作用域在块作用域以内,当生存期结束,就会销毁
{
}
void TestFun2(const Test& t)
{
}
//将对象t拷贝构造到一个临时对象中去
Test TestFun3(const Test& t)
{
//所返回的对象与对象t是不同的
return t;
}
const Test& TestFun4(const Test& t)
{
//return const_cast<Test&>(t);//去除const属性
return t;
}
//TestFun4也可以这么写:
// Test& TestFun4(const Test& t)
// {
// return const_cast<Test&>(t);//去除const属性
// return t;
// }
int main(void)
{
Test t(10);
//t = TestFun3(t);//产生一个临时对象,将临时对象赋值给t(调用等号运算符),接着临时对象会马上销毁
//Test t2 = TestFun3(t);//在返回对象的时候要调用拷贝构造函数,构造了一个临时对象,临时对象被t2接管
//TestFun3(t)产生的临时对象不会马上销毁,临时对象改名字成为了t2有名对象,会被有名对象t2接管
//Test& t2 = TestFun3(t);//Destroy在....之后,临时对象不会马上销毁,这里的拷贝构造函数是在函数返回对象的时候调用的
//Test t2 = TestFun4(t);//返回对象的时候,并没有调用拷贝构造函数,返回对象的引用,然后将对象初始化到t2,调用拷贝构造函数
const Test& t2 = TestFun4(t);//不再调用拷贝构造函数,因为是引用t2接收的,引用要和返回的对象t共享一个内存空间
cout<<"........"<<endl;
return 0;
}
//t = TestFun3(t);//产生一个临时对象,将临时对象赋值给t,接着临时对象会马上销毁
//Test t2 = TestFun3(t);//TestFun3(t)产生的临时对象不会马上销毁,会被有名对象t2接管
//Test& t2 = TestFun3(t);
//Test t2 = TestFun4(t);//返回对象的时候,并没有调用拷贝构造函数,返回对象的引用,然后将对象初始化到t2,调用拷贝构造函数
const Test& t2 = TestFun4(t);//不再调用拷贝构造函数,因为是引用t2接收的,引用要和返回的对象t共享一个内存空间