关于 C++指针的困惑

2019-08-10 22:31:26 +08:00
 Tony042

最近在学习 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 对象,从而更改值。我想知道我这种方法的缺陷在哪里,为什么不能直接更改指针所指向的对象,而是要新建一个临时指针,还请大家帮助我

1667 次点击
所在节点    问与答
16 条回复
AlohaV2
2019-08-10 22:43:42 +08:00
没看过原书细节。你的实现相当于 this.pshp.ps 指向了同一个内存地址,如果原来的对象析构掉了那么(一般情况下我觉得会) hp. ps 会被 delete 掉,那新的这个 ps 也就变成悬挂指针了。
Tony042
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
2019-08-11 00:33:41 +08:00
缺陷倒是没啥缺陷,就是用你这个方法作例子的话不怎么通用,因为你能这样做是因为 string 重载了=,并且如果 ps 是 const 指针,你就没办法这么干了
bccoder
2019-08-11 01:11:54 +08:00
你的 ps 之前如果指向有效地址的话在重新赋值前应该 delete 掉之前的,不知是否正确?
across
2019-08-11 01:37:54 +08:00
例子是深拷贝。你的是浅拷贝,按你做法,包含这么一个指针成员时,再调用赋值,两个类内部指向的是同一个指针对象,在哪个类的析构函数里面释放这个对象内存呢?
across
2019-08-11 01:39:25 +08:00
另外,编译器默认生成的就是浅拷贝,所以你这个实现,其实还不用写····
(我应该没记错来着)
Tony042
2019-08-11 03:54:53 +08:00
@across 我觉的是深拷贝吧,因为我用构造函数(默认构造函数和复制构造函数)的时候都已经重新 new 了一个指针,并没有让指针指向同一块内存啊,我觉得可能就像 @choury 说的那样,不怎么通用,我这样做是 string 重载了
AlohaV2
2019-08-11 07:27:10 +08:00
@Tony042 你的实现应该是浅拷贝。如果换成你的实现,可以试试
HasPtr foo;
{//scpoed
HasPtr bar;
foo =bar;
} // bar dtor
string fools = foo.GetString();
这样会不会出错。并且 foo 构造时申请出的 ps 内存是泄露的。
gggxxxx
2019-08-11 07:39:18 +08:00
楼主的实现确实是深拷贝,也是很常用的一种思路和策略。
楼主的做法和书里做法区别在于,楼主的方案需要依赖 std::string 的拷贝是深拷贝实现。书里的做法不需要依赖。
Tony042
2019-08-11 07:39:45 +08:00
@AlohaV2 我刚才试了下,没问题的,可以成功拷贝数据,GetString 也可以正常输出数据,bar 和 foo 也正常析构了。
Tony042
2019-08-11 07:57:13 +08:00
@gggxxxx 我也觉得是这样,之前 google 了不少,发现大家都是书中这种做法,就搞得我有点懵,久闻 C++深坑居多,怕不小心一脚踩进去,就发帖来问问大家,谢谢层主的回答
chashao
2019-08-11 08:21:54 +08:00
原来 ps 指向的 string 是不是没释放?
AlohaV2
2019-08-11 08:33:45 +08:00
@Tony042 不好意思我看错代码了;
*ps = *hp。ps 是直接复制字符串了, 你的实现功能上我觉得没什么问题。
choury 和 gggxxxx 说的应该是没错
Tony042
2019-08-11 08:34:50 +08:00
@AlohaV2 还有一个问题求问,改变了 string 的值后,之前那个 string 是不是已经被释放了,没有出现内存泄漏吧?还是有点虚
AlohaV2
2019-08-11 08:39:50 +08:00
@Tony042 之前的 string (代码里的 *hp。ps)会在它自己析构的时候通过上面写的析构函数~HasPtr()释放,这边赋值的时候不会释放。
liberize
2019-08-11 09:49:24 +08:00
你写的没有问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/590823

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX