C++中类的复制控制

每种类型,无论是内置类型还是类类型,都对该类型对象的一组操作进行了定义。
每种类型还定义了创建该类型的对象时会发生什么……
构造函数:该类类型对象的初始化。
当定义一个新类型的时候,需要显式或者隐式地指定复制,赋值和撤销该类型的对象时会发生什么,这是通过定义特殊成员:复制构造函数,赋值操作符和析构函数来达到。如果没有显式定义,编译器会为我们定义。

复制构造函数(拷贝构造函数):
具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,常显式使用复制构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。

析构函数:是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动调用析构函数。析构函数用于释放对象构造时构造或者在对象的生命周期中所获取的资源。不管是否定义自己的析构函数,编译器都自动执行类中非static数据成员的析构函数。

赋值操作符:赋值操作符可以通过指定不同类型的右操作数而重载。
右操作数为类型的版本比较特殊,如果我们没有编写这种函数,编译器将为我们合成一个。

实现复制控制操作最困难的部分,往往在于识别何时需要覆盖默认版本,有一种特别常见的情况需要类定义自己的复制控制成员: 类具有指针成员

复制构造函数可用于:
1 根据另一个同类型的对象显示或隐式初始化一个对象。
2 复制一个对象,将它作为实参传给一个函数
3 从函数返回时复制一个对象;
4 初始化容器中的元素
5 根据元素初始化列表初始化数组元素



C++支持两种初始化形式:直接初始化和复制初始化
直接初始化:int ival(1024);
复制初始化:int ival=1024;

对于类类型对象而言
直接初始化直接调用与实参匹配的构造函数
复制初始化总是调用复制构造函数

复制初始化首先指定构造函数创建一个临时对象,然后用复制构造函数将那个临时创建的对象复制到正在创建的对象。


合成复制构造函数:
如果我们没有定义复制构造函数,编译器就会为我们合成一个。即使我们定义了其他构造函数,也会合成复制构造函数,合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
所谓“逐个成员”,指的是编译器将现有对象的每个非static成员,依次复制到正在创建的对象。复制的方法为:每个成员的类型决定了复制该成员的含义, 合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。数组成员的复制是个例外,虽然一般不能复制数组,但是如果一个类具有数组成员,则合成复制构造函数将复制数组,复制数组时合成复制构造函数将复制数组的每一个元素。

定义复制构造函数最难的部分在于认识到需要复制构造函数!!!,只要能认识到需要复制构造函数,定义构造函数一般非常简单。与类同名,没有返回值

禁止复制
有些类需要完全禁止复制,比如iostream。如果想要禁止复制,似乎可以省略复制构造函数,但是!!如果不定义复制构造函数,编译器将合成一个。
为了防止复制,类必须显式声明其复制构造函数为private 。-------但是,类的友元和成员仍然可以复制,如果想连友元和成员中的复制也禁止,就可以 声明一个private复制构造函数但不对其进行定义

声明而不定义成员函数是合法的。通过声明(但不定义)private复制构造函数:
用户代码中的复制尝试将在编译时标出错误,而成员函数和友元中的复制尝试将在链接时导致错误。

如果定义了复制构造函数,也必须定义默认构造函数。。。


赋值操作符:
与复制构造函数一样,如果类没有定义自己的赋值操作符,则编译器会合成一个。。。。


大多数成员函数可以定义为成员函数或非成员函数。当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。


复制和赋值常一起使用,如果需要其中一个,我们几乎也可以肯定需要另一个


析构函数:
动态分配的对象只有在指向该对象的指针被删除时才撤销,如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就一直存在,从而导致内存泄漏。

容器中的元素总是按照逆序撤销。

三法则:如果类需要析构函数,则它也需要赋值操作符合复制构造函数,这是一个有用的经验法则。

编译器总会为我们合成一个析构函数。即使编写了自己的析构函数。。
合成析构函数并不删除指针成员所指向的对象。

代码示例:

#include <iostream>
#include <cstring>

using namespace std;

class NameStr
{
public:
	NameStr()
	{//构造函数
		static const char s_szDefaultName[]="Default name";
		static const char s_szDefaultStr[]="Default string";
		m_pName=new char[strlen(s_szDefaultName)+1];
		m_pData=new char[strlen(s_szDefaultStr)+1];
		strcpy(m_pName, s_szDefaultName);
		strcpy(m_pData, s_szDefaultStr);
	}
	
	NameStr(const char *pName, const char *pData)
	{//构造函数重载
		m_pName=new char[strlen(pName)+1];
		m_pData=new char[strlen(pData)+1];
		strcpy(m_pName, pName);
		strcpy(m_pData, pData);
	}

	NameStr(const NameStr& other)
	{//拷贝构造函数
		m_pName=new char[strlen(other.m_pName)+1];
		m_pData=new char[strlen(other.m_pData)+1];
		strcpy(m_pName, other.m_pName);
		strcpy(m_pData, other.m_pData);
	}
	NameStr& operator=(const NameStr& other)
	{//赋值函数
		m_pName=new char[strlen(other.m_pName)+1];
		m_pData=new char[strlen(other.m_pData)+1];
		strcpy(m_pName, other.m_pName);
		strcpy(m_pData, other.m_pData);
		return *this;

	}	
	~NameStr()
	{//析构函数
		delete[] m_pName;
		delete[] m_pData;
	}

	void Print()
	{
		cout << "Name: " << m_pName << endl;
		cout << "String: " << m_pData << endl; 
	}

private:
	char *m_pName;
	char *m_pData;
};

int main()
{
	NameStr *pDefNss=NULL;

	pDefNss=new NameStr[10];
	NameStr ns("Kingsoft string", "This is for test");
	ns.Print();
	NameStr myNew;
	myNew=ns;
	myNew.Print();
	delete[] pDefNss;
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值