V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Tony042
V2EX  ›  问与答

关于 C++指针的困惑

  •  1
     
  •   Tony042 · 2019-08-10 22:31:26 +08:00 · 1652 次点击
    这是一个创建于 1931 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在学习 C++ primer,在读到 copy-control 这一章节是对指针有了些疑惑,还请大家帮助解答。 这个是书中对 copy assignment operator 的实现,

    HasPtr& operator=(const HasPtr &hp) {
            auto new_p = new std::string(*hp.ps);
            delete ps;
            ps = new_p;
            i = hp.i;
            return *this;
        }
    

    这个是我自己的实现,

    HasPtr &HasPtr::operator=(const HasPtr &hp)
    {
        if (this != &hp)
        {
            *ps = *hp.ps;
            i = hp.i;
        }
        return *this;
    }
    

    两者的区别是,书中的实现方式是先 new 了一个局部指针当内存申请成功后,再删除旧指针指向的对象,并把旧指针指向新指针的位置,我的实现是直接修改指针指向的 string 对象,从而更改值。我想知道我这种方法的缺陷在哪里,为什么不能直接更改指针所指向的对象,而是要新建一个临时指针,还请大家帮助我

    第 1 条附言  ·  2019-08-10 23:04:20 +08:00

    全部实现代码

    class HasPtr
    {
    public:
        HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) {}
        HasPtr(const HasPtr &);
        string GetString() { return *ps; };
        HasPtr &operator=(const HasPtr &);
        ~HasPtr() { delete ps; }
    
    private:
        std::string *ps;
        int i;
    };
    
    HasPtr::HasPtr(const HasPtr &orig) : ps(new std::string(*orig.ps)), i(orig.i)
    {
    }
    
    HasPtr &HasPtr::operator=(const HasPtr &hp)
    {
        auto new_p = new string(*hp.ps);
        delete ps;
        ps = new_p;
        i = hp.i;
        return *this;
    }
    
    16 条回复    2019-08-11 09:49:24 +08:00
    AlohaV2
        1
    AlohaV2  
       2019-08-10 22:43:42 +08:00 via Android
    没看过原书细节。你的实现相当于 this.pshp.ps 指向了同一个内存地址,如果原来的对象析构掉了那么(一般情况下我觉得会) hp. ps 会被 delete 掉,那新的这个 ps 也就变成悬挂指针了。
    Tony042
        2
    Tony042  
    OP
       2019-08-10 23:03:10 +08:00
    @AlohaV2 不好意思,刚才没有放出全部代码
    ```C++
    class HasPtr
    {
    public:
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) {}
    HasPtr(const HasPtr &);
    string GetString() { return *ps; };
    HasPtr &operator=(const HasPtr &);
    ~HasPtr() { delete ps; }

    private:
    std::string *ps;
    int i;
    };

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

    HasPtr &HasPtr::operator=(const HasPtr &hp)
    {
    auto new_p = new string(*hp.ps);
    delete ps;
    ps = new_p;
    i = hp.i;
    return *this;
    }
    ```
    这两个指针我在初始化的时候已经指向了不同的地址,我感觉我在做*this.ps=*hp.ps 的时候是将 this 指向的地址的内容改变了,是改变了 this.ps 指向的 string 的值,而并没有改变指针的值,也就是说并没有改变指针的地址,所以不会造成悬挂指针吧?
    choury
        3
    choury  
       2019-08-11 00:33:41 +08:00
    缺陷倒是没啥缺陷,就是用你这个方法作例子的话不怎么通用,因为你能这样做是因为 string 重载了=,并且如果 ps 是 const 指针,你就没办法这么干了
    bccoder
        4
    bccoder  
       2019-08-11 01:11:54 +08:00 via iPhone
    你的 ps 之前如果指向有效地址的话在重新赋值前应该 delete 掉之前的,不知是否正确?
    across
        5
    across  
       2019-08-11 01:37:54 +08:00
    例子是深拷贝。你的是浅拷贝,按你做法,包含这么一个指针成员时,再调用赋值,两个类内部指向的是同一个指针对象,在哪个类的析构函数里面释放这个对象内存呢?
    across
        6
    across  
       2019-08-11 01:39:25 +08:00
    另外,编译器默认生成的就是浅拷贝,所以你这个实现,其实还不用写····
    (我应该没记错来着)
    Tony042
        7
    Tony042  
    OP
       2019-08-11 03:54:53 +08:00
    @across 我觉的是深拷贝吧,因为我用构造函数(默认构造函数和复制构造函数)的时候都已经重新 new 了一个指针,并没有让指针指向同一块内存啊,我觉得可能就像 @choury 说的那样,不怎么通用,我这样做是 string 重载了
    AlohaV2
        8
    AlohaV2  
       2019-08-11 07:27:10 +08:00 via Android
    @Tony042 你的实现应该是浅拷贝。如果换成你的实现,可以试试
    HasPtr foo;
    {//scpoed
    HasPtr bar;
    foo =bar;
    } // bar dtor
    string fools = foo.GetString();
    这样会不会出错。并且 foo 构造时申请出的 ps 内存是泄露的。
    gggxxxx
        9
    gggxxxx  
       2019-08-11 07:39:18 +08:00
    楼主的实现确实是深拷贝,也是很常用的一种思路和策略。
    楼主的做法和书里做法区别在于,楼主的方案需要依赖 std::string 的拷贝是深拷贝实现。书里的做法不需要依赖。
    Tony042
        10
    Tony042  
    OP
       2019-08-11 07:39:45 +08:00
    @AlohaV2 我刚才试了下,没问题的,可以成功拷贝数据,GetString 也可以正常输出数据,bar 和 foo 也正常析构了。
    Tony042
        11
    Tony042  
    OP
       2019-08-11 07:57:13 +08:00
    @gggxxxx 我也觉得是这样,之前 google 了不少,发现大家都是书中这种做法,就搞得我有点懵,久闻 C++深坑居多,怕不小心一脚踩进去,就发帖来问问大家,谢谢层主的回答
    chashao
        12
    chashao  
       2019-08-11 08:21:54 +08:00 via iPhone
    原来 ps 指向的 string 是不是没释放?
    AlohaV2
        13
    AlohaV2  
       2019-08-11 08:33:45 +08:00
    @Tony042 不好意思我看错代码了;
    *ps = *hp。ps 是直接复制字符串了, 你的实现功能上我觉得没什么问题。
    choury 和 gggxxxx 说的应该是没错
    Tony042
        14
    Tony042  
    OP
       2019-08-11 08:34:50 +08:00
    @AlohaV2 还有一个问题求问,改变了 string 的值后,之前那个 string 是不是已经被释放了,没有出现内存泄漏吧?还是有点虚
    AlohaV2
        15
    AlohaV2  
       2019-08-11 08:39:50 +08:00   ❤️ 1
    @Tony042 之前的 string (代码里的 *hp。ps)会在它自己析构的时候通过上面写的析构函数~HasPtr()释放,这边赋值的时候不会释放。
    liberize
        16
    liberize  
       2019-08-11 09:49:24 +08:00 via Android
    你写的没有问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2732 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 12:13 · PVG 20:13 · LAX 04:13 · JFK 07:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.