正确地复制对象--oeprator=与copy构造函数

额,这个名字有点怪怪的=_=

ok,下面进入正题,为了演示方便,代码只写出简略的部分。

copy构造函数

class Base
{
public:
    Base() {}
    Base(const Base& )
    {
        cout<<"Base copy "<<endl;
    }
};
class Derived: public Base
{
public:
    Derived() {}
    Derived(const Derived& ind) {}
};

重点关注一下复制构造函数。

虽然类中没有任何数据–但是我们说了,为了演示方便–现在假设base 类正确地编写了自身的变量复制。

当有下面的代码的时候:

    Derived d;
    Derived s(d);

我们希望它的行为是,会输出

Base copy

当然了,当发生derived class 复制构造时,我们也希望其base class 的复制构造能够正确地运行。

然后真实的行为却是,没有输出。

因为,当我们必须为derived class 编写复制构造函数的时候,必须小心的复制那些base class 的成分[1]。

正确的编写应该是:

class Derived: public Base
{
public:
    Derived() {}
    Derived(const Derived& ind):Base(ind) {} //在这里调用base class 的构造函数
};

与复制相关的另一个类成员函数就是operator=了。

operator=操作符

在上文中,已经指出,当我们必须要自己去编写copy 构造函数时,要小心地复制对象。

在自己编写operator= 操作符时也一样,下面直接给出代码:

class Base
{
public:
    Base() {}
    Base(const Base& )
    {
        cout<<"Base copy "<<endl;
    }
    Base& operator = (const Base&) //令operator = 返回自身的引用是一个正确的做法
    //为什么?自己去查查资料吧:)
    {
        cout<<"Base = "<<endl;
        return *this;
    }
};
class Derived: public Base
{
public:
    Derived() {}
    Derived(const Derived& ind):Base(ind) {}
    Derived& operator = (const Derived& ind)
    {
        Base::operator=(ind);  //!!这个不能漏掉!!
        return *this;
    }
};

*类中的输出只是为了更好地看到类的行为,仅为演示而用,实际上没有什么意义。

看到红色的注释那句了吗,注意不要漏掉。

也许你又有疑问,那岂不是以后每次为derived class 编写时,都要自己去编写copy 构造函数和operator = 操作符,即使编译器提供的默认版本就足够满足我们的需求?

这个不用担心,编译器会正确地处理的,不如你自己试试:)

然而,operator = 的情况更复杂一点,要注意在operator = 中处理自我赋值。

现在我们单独取一个类作为例子,并给它安上一个指针,指向某个资源。嗯…………差不多像是这样的:

struct T
{
    int i;
};

class Base
{
    T* p;

    Base(const Base&);
public:
    Base()
    {
        p = new T{3};
    }
    ~Base()
    {
        delete p;
    }
    Base& operator = (const Base& inb)
    {
        delete p;
        p = new T(*inb.p);
        return *this;
    }
    void print()  //这个函数是为了验证结果
    {
        cout<<p->i<<endl;
    }
};

ok,现在有这样的代码使用它们:

    Base b1;
    Base b2;
    b2 = b1;
    b2 = b2;//注意这句
    b2.print(); 

虽然写的类没什么意义,但是单看使用的话,我们期待它输出3,是吧。

但是真实的行为是:0。

原因就在于,在operator = 中没有正确地处理自我赋值。

当有b2 = b2这样的语句时(看下面注释)

    Base& operator = (const Base& inb)
    {
        delete p;
        //此时inb == *this, delete p 的时候,其实也删除了自身的资源
        //那么后面的行为就是不确定的了
        p = new T(*inb.p);
        return *this;
    }

怪异的是,运行时竟然没有报错,程序一如既往,兴高采烈地运行,虽然行为不可预知。

那么处理这个问题可以加入自我证同测试:

    Base& operator = (const Base& inb)
    {
        if(this == &inb)    return *this; //自我证同测试
        delete p;
        p = new T(*inb.p);
        return *this;
    }

那么代价是什么?

引入了一个控制分支。

问题是,自我赋值的发生概率会有多大呢?

如果每次都要进行一次判断,势必会影响效率。

于是,聪明的人想到了第二种做法:copy and swap技术

    Base& operator = (const Base& inb)
    {
        T* temp = new T(*inb.p);
        delete p;
        p = temp;
        return *this;
    }

先构造一份副本,然后在交换指针值。

就算自我赋值也不会影响正确的行为。

如果你有顾虑到,当自我赋值的时候,先构造一份自身的副本,然后又给回自己,会不会多此一举?

你想一下自我赋值的发生概率,然后再考虑一下这样处理是否值得:)

[参考资料]

[1] Scott Meyers 著, 侯捷译. Effective C++ 中文版: 改善程序技术与设计思维的 55 个有效做法[M]. 电子工业出版社, 2011.

(条款11:在operator= 中处理“自我赋值”;

条款12:复制对象时勿忘其每一个成分)

时间: 2024-08-26 23:43:12

正确地复制对象--oeprator=与copy构造函数的相关文章

条款12:复制对象时勿忘其每一个成分

对象复制操作operator=或copy构造函数,一定要记得复制对象每一个成份,特别是base class的成分: 注意:

Effective C++_笔记_条款12_复制对象时勿忘其每一个成分

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 编译器会在必要时候为我们的classes创建copying函数,这些“编译器生成版”的行为:将被烤对象的所有成员变量都做一份拷贝. 如果你声明自己的copying函数,意思就是告诉编译器你并不喜欢缺省实现中的某些行为.编译器仿佛被冒犯似的,会以一种奇怪的方式回敬:当你的实现代码几乎必然出错时却不告诉你.所以自己实现copying函数时,请遵循一条规则:如果你为c

复制对象 copy 与mutable copy

转载 :  http://blog.csdn.net/u010962810/article/details/18887841 通过copy方法可以创建可变对象或不可变对象的不可变副本,对于不可变副本,其对象的值不可以改变. 通过mutableCopy方法可以创建可变对象或不可变对象的可变副本,对于可变副本其对象是可变的. 复制分为浅复制和深复制两种:浅复制只是复制对象的引用,并没有复制对象的具体内容.深复制则创建了要复制对象的具体内容,并返回对象副本的引用. 对于复制Foundation中的对象

关于:1.指针与对象;2.深浅拷贝(复制);3.可变与不可变对象;4.copy与mutableCopy的一些理解

最近对深浅拷贝(复制)做了一些研究,在此将自己的理解写下来,希望对大家有所帮助.本人尚处在摸索阶段,希望各位予以指正. 本文包括如下方向的探索: 1.指针与对象: 2.深/浅拷贝(复制): 3.可变/不可变对象: 4.Objective-C中的copy与mutableCopy方法. 一.指针与对象 在初始学习编程的时候,对于绝大多数面向对象编程的语言,这都是个绕不开的重点与难点.非常惭愧的是对它的认识我一直是不够的,并且感觉这项技术有许多的内容可以挖掘.说这是面向对象编程的核心思想也不为过.很多

复制对象(一)copy和mutableCopy方法

本文转载至 http://www.tuicool.com/articles/Fn6rMn CSDN博客原文  http://blog.csdn.net/u010962810/article/details/18887841 通过copy方法可以创建可变对象或不可变对象的不可变副本,对于不可变副本,其对象的值不可以改变. 通过mutableCopy方法可以创建可变对象或不可变对象的可变副本,对于可变副本其对象是可变的. 复制分为浅复制和深复制两种:浅复制只是复制对象的引用,并没有复制对象的具体内容

Python3标准库:copy复制对象

1. copy复制对象 copy模块包括两个函数copy()和deepcopy(),用于复制现有的对象. 1.1 浅副本 copy()创建的浅副本(shallow copy)是一个新容器,其中填充了原对象内容的引用.建立list对象的一个浅副本时,会构造一个新的list,并将原对象的元素追加到这个list. import copy import functools @functools.total_ordering class MyClass: def __init__(self, name):

Effective C++读书笔记之十二:复制对象时勿忘其每一个成分

Item 12:Copy all parts of an object 如果你声明自己的copying函数,意思就是告诉编译器你并不喜欢缺省显示中的某些行为.而编译器会对"你自己写出copying函数"做出一种复仇的行为:既然你拒绝它们为你写出copying函数,如果你的代码不完全,它们也不会告诉你.结论很明显:如果你为class添加一个成员变量,你必须同时修改copying函数.如果你忘记,编译器不太可能提醒你. 一下提供一种正确的模版: class Date{...}; class

EC笔记:第二部分:12、复制对象时勿忘其每一个成分

EC笔记:第二部分:12.复制对象时勿忘其每一个成分 1.场景 某些时候,我们不想使用编译器提供的默认拷贝函数(包括拷贝构造函数和赋值运算符),考虑以下类定义: 代码1: class Point{ private: double x; double y; public:         Point()=default; Point(const Point &other){ x=other.x; y=other.y; } const Point& operator=(const Point

条款12:复制对象的时候不要忘了其每一个部分

首先要说的是当对一个已经定义了构造函数以及赋值运算符(拷贝)的class增加了成员的时候,一定要记得为这个class重新改写构造函数以及拷贝赋值运算符. 下一个容易遗忘的点在继承中,看下面的代码: 1 class Customer 2 { 3 public: 4 //... 5 Customer(const Customer & rhs):name(rhs){} 6 Customer & operator=(const Customer & rhs) 7 { 8 //... 9 }