C++ 构造函数、复制构造函数,拷贝构造函数(深拷贝、浅拷贝)


前言

提示:C++是一种面向对象的编程语言:

C++是C语言的继承,它是一种使用非常广泛的计算机编程语言,C++作为一种静态数据类型检查的、支持多范型的通用程序设计语言,能够支持过程化程序设计、数据抽象化、面向对象程序设计、泛型程序设计、基于原则设计等多种程序设计风格。C++的编程领域众广,常用于系统开发,引擎开发等应用领域,深受广大程序员的喜爱。C++类有固定的结构,其中包含构造、析构函数、成员变量等,今天主要讲解C++类中的构造函数。


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是构造函数?

构造函数是C++中每个类都有的特殊的成员函数,要注意它的作用是初始化对象。
它有下面几个特征:

  • 函数名称与类名相同
  • 无返回值
  • 创建类类型的对象时(对象实例化时)由编译器自动调用,保证每个数据成员都有一个合适的初始值
  • 在该对象的整个生命周期中只会调用一次
  • 构造函数支持函数重载

二、构造函数的分类

构造函数主要可分为四类:

一个测试类(示例):


class test
{
public:
	test(){}; 	//默认构造函数(无参数)
	test(int n, int a) :num(n), age(a) {} //初始化构造函数(有参数)
	test(test& object) //复制构造函数(又叫拷贝构造)
	test(int s) //转换构造函数
	~test(); //析构函数
private:
	int num;
	int age
  • 默认构造函数,又名缺省构造函数(无参构造函数)
    test()构造函数中不处理的话,通常不写
  • 初始化构造函数(有参构造函数
    test(int a,int b);//有参数
  • 复制构造函数(拷贝构造函数
    test(test& object);//形参是本类对象的引用
  • 转换构造函数
    test(int s) ;//形参是其他类型变量,且只有一个形参

复制构造函数

接下来是我们要讲的重点:复制构造函数。

那么什么情况下会调用复制构造函数呢?

复制构造函数被调用的三种情况

一个Demo类(示例):

#include<iostream>
using namespace std;
class Demo{
public:
    Demo(int a,int b){
        num1= a; num2= b;
    }
    Demo(const Demo& c){ //最好是加const,不加也可以。
        num1= c.num1; num2= c.num2;
        cout<<"Copy Constructor called"<<endl ;
    }
public:
    int num1;
    int num2;
};
int main(){
    Demo test1(1, 2);
    Demo test2 (test1);  //调用复制构造函数
    cout<<test2.num1<<","<<test2.num2;
    return 0;
}

复制构造函数在以下三种情况下会被调用。

  • (1) 当用一个对象去初始化同类的另一个对象时,会引发复制构造函数被调用。
    例如,下面的两条语句都会引发复制构造函数的调用,用以初始化 test2
Demo test2 (test1);
Demo test2  = test1;

这两句是等价的,它们都是初始化test2
`提示:这里需要注意的是,初始化与赋值的区别:当等号左边变量有类型时,这是初始化,不是赋值。例如:

Demo test2  = test1;//这是初始化
Demo test2 test1;
test = test1; // 这是赋值 
  • (2) 如果一个函数的参数是另一个类的对象,那么当次函数被调用时,类的复制构造函数将被调用。
    换句话说,作为形参的对象,是用复制构造函数初始化的,而且调用复制构造函数时的参数,就是调用函数时所给的实参。

示例如下:

#include<iostream>
using namespace std;
class A{
public:
    A(){};
    A(A & a){
        cout<<"Copy constructor called"<<endl;
    }
};
void Func(A a){ } //参数是A的对象,此时会调用A的复制构造函数
int main(){
    A a;
    Func(a);
    return 0;
}
  • (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语言中的值传递、址传递。同样的,拷贝构造也分两类,分别叫:浅拷贝、深拷贝。

那么我们下面来讲讲,浅拷贝与深拷贝的区别。

三、拷贝构造函数的分类

  • 拷贝构造也分两类,分别叫:浅拷贝、深拷贝。

深、浅拷贝构造函数的区别

浅拷贝: 在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数
深拷贝: 当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝

浅拷贝和深拷贝主要区别:

复制指针时是否重新创建内存空间。如果没有没有创建内存只赋值地址为浅拷贝,  
创建新内存把值全部拷贝一份就是深拷贝。浅拷贝在类里面有指针成员的情况下只会复制指针的地址,会导致  
两个成员指针指向同一块内存,这样在要是分别delete释放时就会出现问题,因此需要用深拷贝。

提示: 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。:

示例代码说明

浅拷贝:

class A 
{ 
public: 
	A(int _data) : data(_data){} 
	A(){}
private: 
	int data;
 };
int main() 
{ 
	A a(10), b = a; // 仅仅是数据成员之间的赋值 
	std::cout <<first a.data = << a.data<<std::endl;
	b.data = 20
	std::cout <<then a.data = << a.data<<std::endl;

}
运行结果:
first a.data = 10
then a.data = 20
对象之间的拷贝,其实就是对象成员之间的各个成员的赋值。
对象a,与对象b。 b=a即为把 a中的data数据,赋值给了b中的data

提示: 浅拷贝,时两个对象共同使用一块内存空间。如图C语言中的址传递,当一个对象改变了成员中的变量时,另一个对象中的相同成员变量也会相应改变。从上面的demo中可以看出,a中的data被b对象改变从而影响了原来a中data的值。因此只需要记住: 浅拷贝会引起成员变量的改变

如果对象中没有其他的资源(如:堆,文件,系统资源等),则深拷贝和浅拷贝没有什么区别。
否则就需要使用深拷贝。

代码示例:

class A 
{ 
public: 
	A(int _size) : size(_size)
	{
		data = new int[size];
	} // 假如其中有一段动态分配的内存 
	A(){};
	 ~A()
	{
		delete [] data;
	} // 析构时释放资源
private: 
	int* data;
	int size; 
}
int main() 
{ 
	A a(5), b = a; // 注意这一句 
}
这里b的指针data和a的指针指向了堆上的同一块内存,a和b析构时,b先把其data指向的动态分配的内存  
释放了一次,而后a析构时又将这块已经被释放过的内存再释放一次。对同一块动态内存执行2次以上释放  
的结果是未定义的,所以这将导致内存泄露或程序崩溃.所以这里就需要深拷贝来解决这个问题,深拷贝  
指的就是当拷贝对象中有对其他资源(如堆、文件、系统等)的引用 时 (引用可以是指针或引用)时,对  
象的另开辟一块新的资源,而不再对拷贝对象中有对其他资源的引用的指针或引  用进行单纯的赋值

深拷贝:
代码示例:

class A 
{ 
public: 
	A(int _size) : size(_size)
	{
		data = new int[size];
	} // 假如其中有一段动态分配的内存 
	A(){};
	A(const A& _A) : size(_A.size)
	{
		data = new int[size];
	} // 深拷贝 
	~A()
	{
		delete [] data;
	} // 析构时释放资源
private: 
	int* data; 
 	int size;
 }
int main() 
{ 
	A a(5), b = a; // 这次就没问题了 
}

总结

  • 深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝。
  • 选择使用深拷贝还是浅拷贝,取决于类中是否包含(如:堆,文件,系统资源等) ,否则使用深拷贝还是浅拷贝没有本质的区别

以上是最近接触到c++深拷贝和浅拷贝的时候,个人的学习总结记录。后面再遇到问题,再总结

  • 7
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值