文章目录

  • ​​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;
}
  • 类声明及定义如上
  • 测试:
  • (P14)构造函数与析构函数:拷贝构造函数_拷贝构造函数

  • 当函数的返回值是类对象,函数执行完成返回调用者时使用拷贝构造函数。
    理由也是要建立一个临时对象中,再返回调用者。为什么不直接用要返回的局部对象呢?
    因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。
  • 所谓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,接着临时对象会马上销毁

(P14)构造函数与析构函数:拷贝构造函数_后端_02

//Test t2 = TestFun3(t);//TestFun3(t)产生的临时对象不会马上销毁,会被有名对象t2接管

(P14)构造函数与析构函数:拷贝构造函数_Test_03

//Test& t2 = TestFun3(t);

(P14)构造函数与析构函数:拷贝构造函数_开发语言_04

//Test t2 = TestFun4(t);//返回对象的时候,并没有调用拷贝构造函数,返回对象的引用,然后将对象初始化到t2,调用拷贝构造函数

(P14)构造函数与析构函数:拷贝构造函数_拷贝构造函数_05

const Test& t2 = TestFun4(t);//不再调用拷贝构造函数,因为是引用t2接收的,引用要和返回的对象t共享一个内存空间

(P14)构造函数与析构函数:拷贝构造函数_c++_06