我们都知道两个指针指向同一个变量时如果一个指针被释放那么另一个就会出问题

为了说明问题我做了一个很恶心的小例子

class C
{
public :
    C(int v)
    {
        ptrInt=new int;
        *ptrInt=v;

        valueInt = v;
    }

    ~C()
    {

    }
    void DelIntV()
    {
        valueInt=;
        delete ptrInt;
    }

    C(const C& c)
    {

    }
    int * ptrInt;
    int valueInt;
private:

};

int main()
{
    C c1();
    C c2();
    c2=c1;
    std::cout<<"ptrInt "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    c1.DelIntV();

    std::cout<<"address  "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    std::cin.get();
    ;
}

这是把c1赋值给了c2后把指针ptrInt的值输出和valueInt输出,再把c1的指针给delete,valueInt赋值为0

再输出c2的ptrInt和valueInt就会发现指针有问题,看一下输出结果:

已经不对了吧。

为了解决这样的问题我第一个想到的就是重载操作符=

C& operator=(const C &c)
    {
        if(this!=&c)
        {
            delete ptrInt;
            ptrInt = new int;
            *ptrInt= *c.ptrInt;
            valueInt=c.valueInt;
        }
        return *this;
    }

完整代码

class C
{
public :
    C(int v)
    {
        ptrInt=new int;
        *ptrInt=v;

        valueInt = v;
    }

    ~C()
    {

    }
    void DelIntV()
    {
        valueInt=;
        delete ptrInt;
    }

    C(const C& c)
    {

    }
    int * ptrInt;
    int valueInt;

    C& operator=(const C &c)
    {
        if(this!=&c)
        {
            delete ptrInt;
            ptrInt = new int;
            *ptrInt= *c.ptrInt;
            valueInt=c.valueInt;
        }
        return *this;
    }
private:

};

int main()
{
    C c1();
    C c2();
    c2=c1;
    std::cout<<"ptrInt "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    c1.DelIntV();

    std::cout<<"address  "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    std::cin.get();
    ;
}

再看一下输出结果:

这下就正确了吧,但是如果 我们在main函数里做一个修改

int main()
{
    C c1();
    C c2=c1;//这里直接赋值
    std::cout<<"ptrInt "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    c1.DelIntV();

    std::cout<<"address  "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    std::cin.get();
    ;
}

这样后错误就又和之前一样了,为什么呢,

编译器将在c类里找一个副本构造器(copy constructor)如果找不到它会自己创建一个,

即使我们对操作符=进行了重载也没有用,由编译器自己创建的副本构造器仍会以"逐们复制"

的方式把c1赋值给c2

这样我们还要重新实现这个副本构造器,

className(const className &cn);

我是这样做的

    C(const C& c)
    {
        *this=c;
    }

这里的=其实就是调用的重载的=方法

完整代码

class C
{
public :
    C(int v)
    {
        ptrInt=new int;
        *ptrInt=v;

        valueInt = v;
    }

    ~C()
    {

    }
    void DelIntV()
    {
        valueInt=;
        delete ptrInt;
    }

    C(const C& c)
    {
        *this=c;
    }
    int * ptrInt;
    int valueInt;

    C& operator=(const C &c)
    {
        if(this!=&c)
        {
            delete ptrInt;
            ptrInt = new int;
            *ptrInt= *c.ptrInt;
            valueInt=c.valueInt;
        }
        return *this;
    }

private:

};

int main()
{
    C c1();
    C c2=c1;//这里直接赋值
    std::cout<<"ptrInt "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    c1.DelIntV();

    std::cout<<"address  "<<c2.ptrInt<<"  value "<<*c2.ptrInt<<std::endl;
    std::cout<<"valueInt "<<c2.valueInt<<std::endl;
    std::cin.get();
    ;
}

结果

c++ 副本构造器的更多相关文章

  1. js--使用构造器函数来新建对象及操作

    通过new操作符来调用函数,来达到访问对象this值得目的,构造器将其创建的对象返回给我们. 直接上代码 //创建构造器函数 function Gadget(name, color){ this.na ...

  2. JavaSE——面向对象与面向过程、类与对象、(属性、方法、构造器)等

    一:面向对象与面向过程 二者都是一种思想,面向对象是相对于面向过程而言的. 面向过程: 1.面向过程思想强调的是过程(动作). 2.在面向过程的开发中,其实就是面向着具体的每一个步骤和过程,把每一个步 ...

  3. Kafka副本管理—— 为何去掉replica.lag.max.messages参数

    今天查看Kafka 0.10.0的官方文档,发现了这样一句话:Configuration parameter replica.lag.max.messages was removed. Partiti ...

  4. Java之类的构造器(反射)

    反射: Java反射机制:指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定的一个对象,都能够调用它的任意一个属性和方法.这种动态获取类的内容以及动态调用对象的 ...

  5. Swift3.0P1 语法指南——构造器

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  6. Duilib源码分析(二)控件构造器—CDialogBuilder

    上一节了解了大体流程,但是界面控件元素是如何被加载.解析.构建.管理.控件消息如何处理的呢?接下来我们将结合控件构造器进行分析: CDialogBuilder:控件构造器,主要用以解析xml配置文件并 ...

  7. python列表副本

    a=[1,2,3] b=[4,5,6] a=a+b #创建含a和b的副本的新列表 a [1, 2, 3, 4, 5, 6] b [4, 5, 6] c=a+b #创建含a和b的副本的新列表c [1, ...

  8. 验证mongodb副本集并实现自动切换primary~记录过程

    接 验证mongodb主从复制过程 1.创建数据目录 同 验证mongodb主从复制过程 的实验一样,本次实验也是采用直接指定启动参数来启动mongodb数据库,本次实验我们需要启动三个数据库,为了与 ...

  9. java面向对象_构造器

    构造器(构造方法):是类中定义的方法. 1)常常用于给成员变量赋值: 2)与类同名,没有返回值类型,也不能写void: 3)在创建对象时被自动调用.所以构造方法的访问修饰符要用public,才能被自动 ...

随机推荐

  1. 在iOS中实现一个简单的画板App

    在这个随笔中,我们要为iPhone实现一个简单的画板App. 首先需要指出的是,这个demo中使用QuarzCore进行绘画,而不是OpenGL.这两个都可以实现类似的功能,区别是OpenGL更快,但 ...

  2. postman使用之四:切换环境和设置读取变量

    postman提供了environment管理功能,想要在多个环境中测试,比如在测试环境.灰度环境.生产环境等,只需要用同样的接口,切换下环境即可,非常方便.具体步骤: 切换环境 1.点击界面右上角的 ...

  3. sharepoint 2013 持续爬网

    能否对所有类型的内容源都使用连续爬网?不能.连续爬网仅适用于 SharePoint 型内容源.所有其他类型的内容源将继续选择增量爬网和完全爬网. 使用连续爬网是否会给存储库增加额外负载?连续爬网的资源 ...

  4. jQuery validate 验证隐藏域

    $.validator.setDefaults({ ignore: '' }); 不要加载jQuery ready中,什么改写changelog ,那是扯蛋

  5. 新浪微博客户端(47)-在TextView中插入表情

    DJEmotionPageView.m // 发送点击广播(和android类似,区别在于android的广播是只要有上下文对象context,就可以发送) // iOS中的通知发送和接收都是通过NS ...

  6. sql 提取数字、字母、汉字

    --提取数字 IF OBJECT_ID('DBO.GET_NUMBER2') IS NOT NULL DROP FUNCTION DBO.GET_NUMBER2 GO )) ) AS BEGIN BE ...

  7. 关于iOS多线程的总结

    关于iOS多线程的总结   在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项.当然也会给出几种多线程的案例,在实际使用中感受它们的区别.还有一点需要说明的是,这篇 ...

  8. 控制Wordpress对搜索引擎的可见性

    网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,这些通过robots.txt体现. wordpress本身没有robots.txt,但是用根目录访问/robots.txt,如果 ...

  9. js获取url参数值

    用过的封装好的js获取url问号后的参数的方法: <script> var Request = new Object(); Request = GetRequest();var error ...

  10. UESTC 916 方老师的分身III --拓扑排序

    做法: 如果有a<b的关系,则连一条a->b的有向边,连好所有边后,找入度为0的点作为起点,将其赋为最小的价值888,然后其所有能到的端点,价值加1,加入队列,删去上一个点,然后循环往复, ...