【问题标题】:Assignment through smart pointer to class members通过智能指针分配给类成员
【发布时间】:2018-03-23 14:07:43
【问题描述】:

我对 C++ 还是很陌生,在探索智能指针时,有一个我无法完全理解的行为。

考虑以下简单类:

class Student {

public:
    int studentID;
    string gender;
    string name;

    Student(int studentID, string gender, string name)
    {
        this->studentID = studentID;
        this->gender = std::move(gender);
        this->name = std::move(name);
    }

    unique_ptr<string> getNameUnique()
    {
        return make_unique<string>(this->name);
    }

    string* getNamePtr()
    {
        return &name;
    }

};

在主代码中,如果我得到指向实例的name的智能指针并尝试修改它,它不会起作用:

auto nameUniquePtr = mary.getNameUnique();
cout << "Before: " << mary.name << "\n";
cout << "Before (through pointer): " << *nameUniquePtr << "\n";
*nameUniquePtr = "Tracy";
cout << "After: " << mary.name << "\n";
cout << "After (through pointer): " << *nameUniquePtr << "\n\n";

产生:

Before: Mary
Before (through pointer): Mary
After: Mary
After (through pointer): Tracy

显然,mary 的 name 成员没有改变。在逐步调试工具时,我发现智能指针指向的地址和成员的实际地址甚至都不一样。但是,如果我直接获取指向同一个成员的指针并尝试更改它的值,它会起作用:

auto namePtr = mary.getNamePtr();
cout << "Before: " << mary.name << "\n";
cout << "Before (through pointer): " << *namePtr << "\n";
*namePtr = "Tracy";
cout << "After: " << mary.name << "\n";
cout << "After (through pointer): " << *namePtr << "\n\n";

产生:

Before: Mary
Before (through pointer): Mary
After: Tracy
After (through pointer): Tracy

即使我直接设置了一个指向成员的智能指针变量并通过它改变值,还是不行:

auto directPtr = make_unique<string>(mary.name);
cout << "Before: " << mary.name << "\n";
*directPtr = "Jessica";
cout << "After: " << mary.name << "\n\n";

产生:

Before: Tracy
After: Tracy

在使用智能指针指向类成员时是否有一些规范或限制?谢谢。

【问题讨论】:

    标签: c++ pointers smart-pointers unique-ptr dereference


    【解决方案1】:

    当你这样做时

    return make_unique<string>(this->name);
    

    您没有返回指向name 的指针,而是创建了一个std::unique_ptr&lt;std::string&gt;,它拥有自己的std::string,它是name 的副本。它与name 完全分开。

    您可以创建一个具有空删除器的unique_ptr,并使用指向name 的指针构造它,但是当您可以通过引用简单地返回name 以获取它的句柄时,这是很多工作。


    我还想指出,您应该使用member initialization list 来初始化您的班级成员。您正在做的是在构造函数主体中进行分配,这不是那么有效。对你来说,看起来像

    Student(int studentID, string gender, string name) : studentID(studentID), gender(std::move(gender)), name(std::move(name)) {}
    

    【讨论】:

    • 非常感谢。为什么如果我通过这样做设置一个 unique_ptr 变量:auto student = make_unique&lt;Student&gt;(mary),并在Student 类中显式设置复制构造函数,当分配此变量时,复制构造函数被调用?那个 unique_ptr 实际上拥有自己的实例?
    • @GaryC。是的。如果您传递给make_unique 的任何内容都用于构造make_unique 正在制作的任何类型。例如int foo; auto bar = make_unique&lt;int&gt;(foo); 初始化一个新的int,它是foo 的副本
    【解决方案2】:

    当你使用你的智能指针时,你每次都创建一个新的智能指针实例,其值为name,所以你的指针实际上并不指向成员变量name的内存地址,而是您创建的副本。用更多注释的术语:

    unique_ptr<string> getNameUnique()
    {
        return make_unique<string>(this->name); //copy the value of name into a new object that is a smart pointer
    }
    

    您可以考虑改用shared_ptr,如下所示:

    class Student {
    
    public:
      std::shared_ptr<std::string> name;      
      //.. other member variables
    
      Student(int studentID, string gender, string name)
      {
        //same as before except for
        this->name = std::make_shared<std::string>(name);
      }
    
      shared_ptr<std::string> getNameShared()
      {
         return this->name;
      }
      //otherwise the same
    };
    

    这会将name 的值存储在类中的智能指针中。然后可以从您的成员函数返回此 shared_ptr,并且该返回值的所有者将拥有指向您的 Student 实例所拥有的相同内存空间的指针。然后,如果您在类之外修改此智能指针的内容,您将在name 成员变量内也观察到该更改。

    【讨论】:

      【解决方案3】:

      如果您考虑以下代码,这可能有助于您理解正在发生的事情:

      return make_unique<string>(this->name);
      

      相当于:

      std::unique_ptr<string> p;
      
      p.reset(new string(this->name));
      
      return p;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-16
        • 1970-01-01
        • 1970-01-01
        • 2021-07-22
        • 1970-01-01
        • 2013-12-10
        • 2011-12-25
        • 2023-01-01
        相关资源
        最近更新 更多