基础语法之六:类的复制控制

1、类定义

类成员:可以包括 数据、函数、类型别名等;

构造函数:初始化列表,在构造函数的形参列表后,由冒号起始,参数间以逗号隔开;

成员函数成员函数必须在类内声明,定义是可选的,类内定义的成员函数默认为inline,成员函数后   添加 const 表示本函数不改变类对象的数据成员,即this指针为指向const对象的指针。

另外this永远是一个 const 指针即只能指向类对象本身,不能人为改变。

 

2、static类成员

2.1 static成员函数

static关键字只能出现在类定义内的成员声明处,类似于explicit。 static成员函数没有this参数所以不能声明为const

 

2.2 static数据成员

staic数据成员必须在类的定义体外部定义(only once),static数据成员不是通过构造函数初始化,而是应该在定义时进行初始化,原因是当类头文件包含在多个源文件中时防止变量的重复定义;

 

特殊的整型const static成员:整型const static成员可以在类定义体中初始化,该成员仍然必须在类外部进行定义,不过不用使用初始化式。也就是说类内即使有初始化式也只是声明和初始化,只有类外才是定义,才会产生内存空间;

 

static成员不是类对象的组成部分

所以static成员类型可以是所属类的类型本身,但是普通成员不可以,普通成员被限定为类类型的指针或引用、或者其他变量类型。

栗子:

class Singleton

{
public:
        static Singleton* getInstance();

private:
        Singleton();
        //把复制构造函数和=操作符也设为私有,防止被复制
        Singleton(const Singleton&);
        Singleton& operator=(const Singleton&);

   static Singleton* instance;

};

 

Singleton* Singleton::instance = new Singleton();
Singleton* Singleton::getInstance()

{
        return instance;
}

 

3、 复制控制

复制控制包括:复制构造函数、析构函数、赋值操作

如果没有定义他们,编译器会自动合成一个,但是编译器合成的复制控制函数属于位复制(浅度复制),如果类包含指针类型,就需要定义自己的复制控制函数,此时应当实现值复制(深度复制)。

 

只有单个形参,而且该形参为本类类型的引用(常用const修饰),这样的构造函数称为复制构造函数。

1. 对象的定义形式

string null_book = "9-999-99999-9";    

// copy-initialization 调用接收C风格字符串的构造函数构造一个string对象

// 然后使用复制构造函数将null_book初始化为临时对象

 

string null_book2 = string() ;     

// copy-initialization 调用默认构造函数构造一个string对象

// 然后使用复制构造函数将 null_book2初始化为该临时对象

 

2.形参与返回值

当形参或返回值类型为类类型时由复制构造函数进行复制。

 

3.初始化容器元素

vector<string> svec(5); 

编译器首先使用默认构造函数,构造一个临时string对象,然后使用复制构造函数将临时值复制到svec每个元素。

 

4.构造函数与数组元素

如果没有为类类型提供数组初始化式,将使用默认构造函数初始化每个元素,如果使用花括号来提供显示初始化式,使用复制构造函数复制到对应元素。

 

3.1 合成的复制构造函数

如果没有定义复制构造函数,编译器就会合成一个,与默认构造函数不同的是,即使提供了其他构造函数,只要没有定义复制构造函数,都会合成一个。合成的复制构造函数其行为是:逐个成员初始化。

 

3.2 定义自己的复制构造函数

当类中具有指针类型,应当自己定义复制构造函数,而且复制构造函数一般不指定为explict,指定了explicit不能使用等号进行隐式调用复制构造函数。

 

3.3 禁止复制

为了禁止复制,类必须显式声明复制构造函数为private,然而类的友元和成员仍可以进行复制;

如果想要连友元和成员也禁止,可以声明一个private复制构造函数而不对其定义,此时用户代码中的复制尝试将会标记为编译错误,友元和成员的复制尝试将在链接期导致错误。

 

3.4 赋值操作符

赋值时,对于一般的都是直接使用合成赋值函数。但是对于成员变量为指针、对象或复制时希望完成其他附加操作的均需自己来处理赋值行为。

 

3.5 析构函数

析构函数与复制构造函数或赋值操作符重要区别,即使定义了自己的析构函数,合成的析构函数仍然运行,在自定义函数之后运行。

 

三法则:需要析构函数,就需要复制构造函数和赋值操作符,因为需要析构函数的场景通常为成员变量为指针、对象或希望完成其他附加操作的处理行为。

  • 常见三种复制是一起出现的,即需要自定义一种复制行为时,往往另外两种也需要自定义
  • 需要禁止复制时,必须显式地声明其复制构造函数为 private(其友元和成员依然可以复制)
  • 如果要连友元和成员的复制也禁止,就可以声明一个private的复制构造函数但不对它进行定义。(如果复制类对象会提示编译错误,如果成员和友元尝试复制就会导致链接错误)
  • 在实现时其实赋值重载函数是包含复制构造函数和析构函数功能的,此时可以将复制和析构功能单独做到两个私有函数中,如演示代码中的CopyData和DeleteData

定义了复制控制行为的栗子:

class CCopyControl

{

public:

CCopyControl(int nData=0);

CCopyControl(const CCopyControl& c); //复制构造函数

CCopyControl& operator=(const CCopyControl& c); //赋值重载函数

~CCopyControl(); //析构函数

 

private:

int m_nData;

int *m_pData;

void CopyData(const CCopyControl& c);

void DeleteData();

};

 

//默认构造函数

CCopyControl::CCopyControl( int nData/*=0*/ )

{

m_nData = nData;

m_pData = new int(nData);

}

 

//复制构造函数

CCopyControl::CCopyControl( const CCopyControl& c )

{

CopyData(c);

}

 

//赋值重载函数

CCopyControl& CCopyControl::operator=( const CCopyControl& c )

{

if (this != &c)

{

DeleteData();

CopyData(c);

}

return *this;

}

 

//析构函数

CCopyControl::~CCopyControl()

{

DeleteData();

}

 

//复制数据

void CCopyControl::CopyData( const CCopyControl& c )

{

this->m_nData = c.m_nData;

this->m_pData = new int(c.m_nData); //深拷贝

}

 

//删除数据

void CCopyControl::DeleteData()

{

if (!m_pData)

{

delete m_pData;

m_pData = NULL;

}

}

 

 

4 、智能指针

智能指针的使用栗子(如下定义中,也可以使指针计数维护类增加封装类为友元):

// CMySmartPtr指针封装类,将普通指针封装为智能指针

template <class T>

class CMySmartPtr 

{

public:

/*构造函数*/

CMySmartPtr(T* pT)

{

pCountT = new CCountT(pT);

}

 

CMySmartPtr()

{

pCountT = NULL;//默认指针为空

}

 

/*复制控制*/

~CMySmartPtr()

{

DeleteData();

}

 

CMySmartPtr(const CMySmartPtr& p)

{

CopyData(p);

}

 

CMySmartPtr& operator=(const CMySmartPtr& p)

{

if (this != &p) //注意自身对自身赋值的情况

{

DeleteData();

CopyData(p);

}

 

return *this;

}

 

/*指针*和->解引用*/

T& operator*()

{

return *(this->pCountT->pT);

}

 

T* operator->()

{

return this->pCountT->pT;

}

 

private:

//装饰类,为待管理的指针维护引用计数

class CCountT

{

public:

CCountT(T* pT)

{

this->pT = pT;

this->nCount = 1;

}

~CCountT()

{

delete pT;

}

T* pT;

int nCount;

};

 

//统一共享的指针,依靠引用计数来释放

private:

CCountT *pCountT;

 

void CopyData(const CMySmartPtr& p)

{

p.pCountT->nCount++;

this->pCountT = p.pCountT;

}

 

void DeleteData()

{

if (pCountT && --pCountT->nCount==0)

{

delete pCountT;

pCountT = NULL;

}

}

};

 

 

来自 <http://www.360doc.com/showweb/0/0/548325597.aspx>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值