【问题标题】:What happens to memory when I dereference a pointer and assign the value to some variable?当我取消引用指针并将值分配给某个变量时,内存会发生什么?
【发布时间】:2013-09-19 19:25:09
【问题描述】:

我有一个具有私有字符串指针的类。此类还有一个公共方法,该方法取消引用该字符串指针并将结果字符串返回给调用者。我的问题是:保存取消引用的字符串的内存会发生什么?

这是我的代码:

#include <iostream>
#include <string>

class myclass
{
    private:
        std::string *_name;

    public:
        myclass()
        {
            this->_name = new std::string("my name");
        }

        ~myclass()
        {
            delete this->_name;
        }

        std::string getName()
        {
            return *this->_name;
        }
};

void main()
{
    myclass m;
    {
        std::string str = m.getName(); //what happens to this memory?
        std::cout << str << std::endl;
    } //str deleted here (I think)
    //here, the m._name pointer hasn't yet been deleted
}

在我的主函数中,变量str 被分配了取消引用的字符串。保存这个字符串的内存会发生什么?我认为内存保留在堆栈上,因此当变量超出范围时会自动“取消分配”。令我困惑的是,即使取消引用的字符串不再在范围内,私有字符串指针仍可能在范围内。是否为 str 变量分配了私有 _name 指针值的副本?

如果这段代码确实在后台复制了字符串,我是否应该关注性能。如果我只是让getName() 返回_name 指针而不取消引用它,我的代码会运行得更快吗?

【问题讨论】:

  • 它显然被复制了..如果您返回私有变量的地址,您将暴露它以被任何外部程序更改。我认为这不是您要在这里寻找的东西..
  • std::string getName() 正在返回一个字符串的副本。 str 值从_name 指向的字符串副本中复制值。更好的方法是将 getName() 定义为 const std::string&amp; getName() const { return *(this-&gt;_name); }
  • 取消引用和解除分配是两件不同的事情:前者我们访问指针指向的内存位置,后者我们释放该特定变量的内存。
  • 离题,但您可能不希望将指向字符串的指针作为成员。直接使用字符串。
  • @user2023861 getName() 没有释放任何东西,您在函数中取消引用字符串然后返回字符串的副本。然后将此副本再次复制到 str 中。返回带有 & 的字符串会导致它返回对 *_name 的引用而不进行复制,然后它将被复制到 str 中。它是const 的原因是为了不破坏封装。如果不是 const,您将可以直接访问 _name 指向的私有成员变量,这可能是您想要的,但它与您当前的 getName() 实现不同。

标签: c++ pointers memory-management dereference


【解决方案1】:

首先,您不需要指针。其次,你可以返回一个副本(就像你做的那样)或一个引用(如果你想坚持的话,也可以返回一个指针),但是这两种方法都有利有弊:

class myclass
{
private:
    std::string _name;
public:
    myclass() : _name("my name")
    {
    }
    std::string getName1() { return _name; }
    std::string& getName2() { return _name; }
    const std::string& getName3() const { return _name; }
}

getName1 按值返回。缺点是会生成一个副本,这会影响性能,但在对象超出范围后返回值可以安全使用。

getName2getName3 的区别在于可以使用前者来修改类的实际成员。缺点是如果对象超出范围,则无法再使用该引用,因此您将留下一个悬空引用。优点是它更高效,因为不涉及复制。

myclass x;
std::string a = x.getName1(); // copy is made
std::string& b = x.getName2(); // no copy
                               // b is mutable
b = "something else";  //this will modify x._name
const std::string& c = x.getName3; // no copy
                                   // c is const

myclass* y = new myclass;
std::string d = x.getName1();  // copy is made
std::string& e = x.getName2(); // no copy
delete y;
d = "something else"; //legal
e = "something else"; //undefined behavior, e is a dangling reference

【讨论】:

  • 如果我这样做std::string str2 = m-&gt;getName2();,(没有与号),str2 会是副本吗?在我的测试中,str2 似乎在delete y 之后仍然存在。
  • @user2023861 是的,它是副本。
【解决方案2】:

遵循“三法则”。如果您定义了以下任何一个:复制构造函数、赋值运算符、析构函数,则定义所有这些。这样就不会混淆指向字符串的指针是否被删除:您只需在相应的赋值运算符中创建一个新的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-16
    • 2012-01-05
    • 2015-02-24
    • 2017-09-15
    • 1970-01-01
    • 2014-12-20
    相关资源
    最近更新 更多