C++的拷贝构造函数、operator=运算符重载,深拷贝和浅拷贝、explicit关键字

1、在C++编码过程中,类的创建十分频繁。

简单的功能,当然不用考虑太多,但是从进一步深刻理解C++的内涵,类的结构和用法,编写更好的代码的角度去考虑,我们就需要用到标题所提到的这些内容。

最近,在看单例模式,觉得十分有趣,然而如果想要掌握单例模式,就必须掌握这些内容。下面是我的一些学习总结,参考了很多博客内容。文末将注明出处。

2、先上代码

 

// testSingleMode.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>

using namespace std;

class Complex
{
private:
	double m_real;
	double m_imag;

public:
	Complex(void){
		m_real = 0.0;
		m_imag = 0.0;
	}

	Complex(double real, double imag){
		m_real = real;
		m_imag = imag;
	}

	Complex(const Complex & c){	//这里就是最经典的拷贝构造函数了
		m_real = c.m_real;
		m_imag = c.m_imag;
	}

	Complex &operator = (const Complex &rhs){	//这里就是最经典的operator=操作符重载了
		if (this == &rhs){
			return *this;
		}

		this->m_real = rhs.m_real;
		this->m_imag = rhs.m_imag;

		return *this;
	}

	explicit Complex::Complex(double r){	//explicit的用法,只适用于1个参数的情况
		m_real = r;
		m_imag = 0.0;
	}

};


int main()
{
	Complex c1, c2;	//调用 第15行 默认无参数的构造函数
	Complex c3(1.0, 2.5);	//调用 第20行 具有2个形参的构造函数
	//Complex c3 = Complex(1.0, 2.5);	//和上一行是一个意思,所以这个注释了
	c1 = c3;	//调用 第30行 重载operator=运算符
	c2 = c3;	//调用 第30行 重载operator=运算符
	//c2 = 5.2;	//隐式转换,需要去掉41行的explicit关键字,才可编译通过

	Complex c5(c2);		//调用 第25行 拷贝构造函数
	Complex c4 = c2;	//调用 第25行 拷贝构造函数

	getchar();
	return 0;
}

 

 

【注1】explicit 只适用于构造函数只含有1个参数的情况,加上这个关键字,意味着不支持构造函数隐式转换,可以避免一些误解。如果去掉这个关键字,那么代码里面的:c2 = 5.2 ; 就是可以执行的了。

【注2】为什么函数中可以直接访问对象c的私有成员?
答:(网上)因为拷贝构造函数是放在本身这个类里的,而类中的函数可以访问这个类的对象的所有成员,当然包括私有成员了。

 

 

 

 

3、上面代码的注释,已经是非常的清楚了

自定义拷贝构造函数是一种良好的编程风格,

它可以阻止编译器形成默认的拷贝构造函数,防止出错。

 

浅拷贝:如果自己不写拷贝构造函数,系统会默认生成一个,而系统的拷贝构造函数是浅拷贝。

深拷贝:自己写一个拷贝构造函数,系统就不会产生了默认的构造函数了(来自网上说法)。自己写的这个拷贝构造函数,当然会有开辟空间的动作,所以是深拷贝。也就是说,如果生成类的实例的时候,调用了自己写的拷贝构造函数,那么在内存空间上,必然是会开辟新的空间,而不用担心只是一个指针。很多时候,我们希望得到的类的实例是各自独立的,各有各的空间。如果希望得到指针,那就不用操心这么多。

 

在某些状况下,类内成员变量需要动态开辟堆内存,如果实行浅拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

 

4、总结

(1)深拷贝:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。反之,没有重新分配资源,就是浅拷贝。

(2)什么时候用到拷贝构造函数

 

  a.一个对象以值传递的方式传入函数体; 
  b.一个对象以值传递的方式从函数返回;
  c.一个对象需要通过另外一个对象进行初始化。
(3)深拷贝好还是浅拷贝好?

如果实行钱拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

 

 

 

 

(4)代码例子2

 

#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

class Person
{
public:

	// 构造函数
	Person(char * pN)
	{
		cout << "一般构造函数被调用 !\n";
		m_pName = new char[strlen(pN) + 1];
		//在堆中开辟一个内存块存放pN所指的字符串
		if (m_pName != NULL)
		{
			//如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它
			strcpy_s(m_pName, strlen(pN) + 1, pN);
		}
	}

	// 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
	Person(Person & chs)
	{
		cout << "拷贝构造函数被调用 !\n";
		// 用运算符new为新对象的指针数据成员分配空间
		m_pName = new char[strlen(chs.m_pName) + 1];

		if (m_pName)
		{
			// 复制内容
			strcpy_s(m_pName, strlen(chs.m_pName) + 1, chs.m_pName);
		}

		// 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
	}

	 系统创建的默认复制构造函数,只做位模式拷贝
	//Person(Person & p)
	//{
	//	//使两个字符串指针指向同一地址位置         
	//	m_pName = p.m_pName;
	//}

	~Person()
	{
		delete m_pName;
	}

	void getName(){
		cout << m_pName << endl;
	}

private:
	char * m_pName;
};

void main()
{
	Person man("lujun");
	man.getName();
	Person woman(man);
	woman.getName();

	getchar();
}


程序运行环境: VS2013 

 

输出结果:

 

 

 

==============

参考文献:

http://blog.csdn.net/lpp0900320123/article/details/39007047

http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html

http://www.cnblogs.com/raichen/p/4752025.html

http://blog.163.com/haixing_03031102/blog/static/120105509200972855328532/

http://blog.csdn.net/waitforfree/article/details/10137495

https://zhidao.baidu.com/question/1638161405180160020.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值