13.2:拷贝控制和资源管理

两种选择:

  • 类的行为像一个值:有自己的状态,拷贝一个像值的对象时,副本和原对象是完全独立的。改变副本不会改变原对象。
  • 类的行为像一个指针:类是共享状态,当拷贝这个对象时,原对象和副本对象使用相同的底层数据,改变副本也会改变原对象。


1.行为像值的类

拷贝对象,而不是拷贝指针。

代码如下:

class HasPtr{

public:

HasPtr(const string &s = string()):ps(new string(s)), i(0) {}

//对ps指向的string,每个HasPtr对象都有自己的拷贝

HasPtr(const HasPtr &p):ps(new string(*p.ps)), i(p.i) {}

HasPtr& operator = (const HasPtr&);

~HasPtr() { delete ps; }

private:

string *ps;

int i;

};

HaPtr& HasPtr::operator = (const HasPtr &rhs)

{

auto newp = new string(*rhs.ps);  //拷贝底层string

delete ps;  //释放旧内存

ps = newp;  //从右侧对象拷贝数据到本对象

i = rhs.i;

return *this;  //返回本对象。

}

注意要防范自赋值操作的错误。

错误代码:

HaPtr& HasPtr::operator = (const HasPtr &rhs)

{

delete ps;  //释放对象指向的string

//但是如果rhs和*this是同一对象,就将从已经释放的内存中拷贝数据

ps = new string(*(rhs.ps));

i = rhs.i;

return *this;  //返回本对象。

}



2.行为像指针的类

需要拷贝指针,而不是拷贝指针所指的对象。

对于共享资源,最好的方法是使用shared_ptr。当时如果希望直接管理资源,最好使用引用计数(使用自定义而非shared_ptr)。

工作方式如下:

  • 在构造函数中,除了初始化对象外,还要创建一个引用计数,来记录共享状态。初始化时只有一个对象,引用计数初始化为1.
  • 拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。并且要递增共享的计数器。
  • 析构函数递减计数器,当计数器变为0时,析构函数就可以释放成员。
  • 拷贝赋值运算符要递增右侧运算对象的计数器,递减左侧对象的计数器。如果左侧运算对象计数器为0,拷贝赋值运算符就必须销毁此对象的状态。

为了更新引用计数,将计数器保存在动态内存中,当创建对象,分配一个新的计数器,拷贝或赋值对象时,拷贝指向计数器的指针。

代码如下:

class HasPtr{

public:

//构造函数分配新的string和新的计数器1

HasPtr(const string &s = string()):ps(new string(s)),i(0),use(new size_t(1)){}

//拷贝构造函数拷贝三个成员,并递增计数器

HasPtr(const HasPtr &p):ps(p.ps), i(p.i), use(p.use) { ++*use; }

HasPtr& operator = (const HasPtr&);

~HasPtr();

private:

string *ps;

int i;

size_t *use; //计数器,记录共享*ps的成员

};

HasPtr& HasPtr::operator = (const HasPtr &rhs)

{

++*rhs.use;  //递增右侧运算对象的引用计数

--*use; //递减本对象的引用计数

if(*use == 0)

{

delete ps; //释放对象

delete use; //释放计数器

}

//拷贝rhs对象

ps = rhs.ps;

i = rhs.i;

use = rhs.use;

return *this;

}

HasPtr::~HasPtr()

{

--*use; //递减对象计数器

if(*use == 0)

{

delete ps; //释放对象

delete use; //释放计数器

}

}

时间: 2024-08-30 12:02:01

13.2:拷贝控制和资源管理的相关文章

【C++ Primer 第13章】2. 拷贝控制和资源管理

拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据,改变副本也会改变原对象. 13.2节练习 1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 class HasPtr { 6 public: 7 HasP

【足迹C++primer】43、拷贝控制和资源管理

拷贝控制和资源管理 13.2.1行为像值的类 *定义一个拷贝构造函数,完成string的拷贝,而不是拷贝指针 *定义一个析构函数来释放string *定义一个拷贝赋值运算符来释放对象当前的string,并从右侧运算对象拷贝string class HasPtr { public: HasPtr(const string &s=string()):ps(new string(s)), i(0){} //对ps指向的string,每个HasPtr对象都有自己的拷贝 HasPtr(const HasP

C++ Primer学习总结 第13章 拷贝控制

第13章 拷贝控制 1.    什么是拷贝构造函数? P440 如果一个类的构造函数的第一个参数是自己类类型的引用, 且所有其他参数都有默认值, 那么这就是一个拷贝构造函数. 2.    拷贝初始化和直接初始化.  P441 如果初始化的时候使用等号"="来初始化一个对象, 那么就是拷贝初始化. 相反, 如果初始化时, 没有用等号"=", 那么就是直接初始化. 如果是拷贝初始化, 那么用的一定是拷贝构造函数(也有可能用移动构造函数). 如果是直接初始化, 那么就用参

拷贝控制和资源管理

拷贝控制和资源管理 通常,管理类外资源的类必须定义拷贝控制成员.有两种选择:可以定义拷贝操作,使类的行为看起来像一个值或者像一个指针. 类的行为像一个值,意味着它应该也有自己的状态.当我们拷贝一个像值的对象时,副本和原对象时完全独立的.改变副本不会对原对象由任何影响,反之亦然. 行为像指针的类则共享状态.当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据.改变副本也会改变原对象,反之亦然. IO类型和unique_ptr不允许拷贝或赋值,因此它们的行为既不像值也不像指针. 赋值运算符

拷贝控制2(拷贝控制和资源管理/交换操作)

为了定义拷贝构造函数和拷贝赋值运算符,我们首先必须确认此类型对象的拷贝语义.通常可以定义拷贝操作,使类的行为看起来像一个值或者像一个指针(即所谓的深拷贝和浅拷贝) 类的行为像一个值,意味着它应该也有自己的状态.当我们拷贝一个像值的对象时,副本和原对象是完全独立的.改变副本不会对原对象有任何影响,反之亦然 行为像指针的类则共享状态.当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据.改变副本也会改变原对象,反之亦然 在我们使用过的标准库类中,标准库容器和 string 类的行为像一个值.

《C++primer(第五版)》学习之路-第十三章:拷贝控制

[ 声明:版权所有,转载请标明出处,请勿用于商业用途.  联系信箱:[email protected]] 13.1 拷贝.赋值与销毁 1.当定义一个类时,我们显式地或隐式地指定在此类型的对象拷贝.移动.赋值和销毁时做什么.一个类通过定义五种特殊的成员函数来控制这些操作,包括:拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符和析构函数. 2.在一个构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化.在一个析构函数中,首先执行函数体,然后销毁成员.成员按

拷贝控制

当定义一个类时,我们显式地或隐式地指定在此类型的对象拷贝.移动.赋值和销毁时做什么.一个类通过定义五种特殊的成员函数来控制这些操作,包括:拷贝构造函数(copy constructor).拷贝赋值运算符(copy-assignment operator).移动构造函数(move constructor).移动赋值运算符(move-assignment operator)和析构函数(destructor).拷贝和移动构造函数定义了当用同类型的另一个对象初始化本对象时做什么.拷贝和移动赋值运算符定义

C++11(12):拷贝控制

拷贝构造函数的第一个参数必须是引用类型,此参数几乎总是const的引用.拷贝构造函数在几种情况下会隐式地使用.因此,拷贝构造函数不应该是explicit的 即使我们定义了其他构造函数,在没有拷贝构造函数时,编辑器也会为我们合成的.编辑器从给定对象中依次将每个非static成员拷贝到创建的对象中.每个成员决定了它使用何种方式进行拷贝.类调用拷贝构造函数,数组逐个拷贝,内置类型直接拷贝 string dots(10,'.')  //直接初始化 string noll_book="999999&quo

Chapter13:拷贝控制

拷贝控制操作:拷贝构造函数.拷贝赋值运算符.移动构造函数.移动赋值运算符.析构函数. 实现拷贝控制操作的最困难的地方是首先认识到什么时候需要定义这些操作. 拷贝构造函数: 如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数时拷贝构造函数. 参数是引用:为了避免陷入递归当中. 拷贝构造函数在几种情况下会被隐式地使用,因此,拷贝构造函数通常不应该是explicit的. 合成的拷贝构造函数:逐个拷贝其数据成员:对类类型的成员,会使用其拷贝构造函数来拷贝,内置类型直接