【问题标题】:C++ | temporary pointers lifetimeC++ |临时指针生命周期
【发布时间】:2021-05-17 21:44:41
【问题描述】:

我正在学习 C++,在我所有的“实验”中,我都在尝试理解临时对象(右值)的生命周期。

我的问题是:

拥有一个包含 const char* 指针的对象,当我想使用以“const char*”作为参数的构造函数时会发生什么?通常临时对象会自动销毁,但是以这种方式创建的指针会发生什么?

我暂时不使用 std::string 或其他 c++11 类,因为我只是专注于理解右值。

以下示例:

class MyAwesomeClass {

private:
    const char* data;

public:
    MyAwesomeClass(const char* ptr) {

        this->data = ptr;
    }

    MyAwesomeClass(MyAwesomeClass&& myAwesomeClassRVALUE) {

        this->data = myAwesomeClassRVALUE.data;
        myAwesomeClassRVALUE.data = nullptr;
    }

    ~MyAwesomeClass() {

        delete data;
    }
};

int main() {

    MyAwesomeClass s = "My Awesome Class' string data.";

    return 0;
}

【问题讨论】:

  • 您有两个不相关的问题。请选择一个并为它创建一个minimal reproducible example
  • 您仍然需要创建一个minimal reproducible example。您询问构造函数中发生的事情。保留构造函数,也许还有 1 个方法来显示您打算如何使用该类。剩下的代码必须走。
  • std::string* stringData; 如果没有非常有说服力的理由,我会拒绝结构/类中的 CL,我可以发明的这些都不适用于这里。
  • 与您的问题无关:: public Object 看起来您正在尝试模拟Java,其中所有类都从一个Object 继承。那是怎么回事?
  • 如果您在其中使用的std::string 构造函数没有这样的保证,为什么您认为可以将复制构造函数实现为noexcept?另外,为什么要使 stringData 成为指针?这只会使内存管理复杂化。 (如果您希望副本引用相同的数据,请使用std::shared_ptr<std::string>()。)

标签: c++ arrays rvalue


【解决方案1】:

通常临时对象会自动销毁,但是以这种方式创建的指针会发生什么?

指针本身就是对象。与所有其他临时对象一样,临时指针对象也会发生同样的事情。

您的示例删除了指向字符串文字的指针,因此程序的行为未定义。即使通过将指针传递给使用new[] 分配的数组来正确使用该类,它也会有一个损坏的赋值运算符。

至于右值,我能找到的示例中唯一的右值表达式是nullptr

【讨论】:

    【解决方案2】:

    快速回答是您的类不拥有数据,而只是原始指针。在某些情况下,您会看到删除运算符出现问题。原始指针不是确保正确对象所有权的好工具。

    【讨论】:

      【解决方案3】:

      注意此答案基于之前对问题的编辑,其中MyAwesomeClassString 类。

      您的String 类并不是真正的字符串类,因为它不拥有底层字符串数据。它更类似于std::string_view

      我认为你有两条途径可以追求:

      1. 您的班级拥有基础数据。在这种情况下,该类是 std::string 的包装器,并且 std::stringdata member. Look up composition. No need to worry about lifetime asstd::string` 表现良好。

      2. 您的类就像指向另一个字符串的“指针/引用/视图”。在这种情况下,您有一个 const char* 可能还有 std::size_t 大小的数据成员。你基本上有一个std::string_view(除了设计std::string_view的智慧、专业知识和经验)。由于您不拥有基础数据,因此您无法对基础数据的生命周期做任何事情。该类的用户必须确保它不会以过期对象的“YourStringView”结尾,就像他/她需要确保它不会以过期对象的引用/指针结尾一样。

      这两种场景的语义大相径庭(作为对象和指向对象的指针之间的区别)。


      无论如何,我不建议您这样做,除非出于学习原因。 std::string_view 已经存在,所以就使用它。如果您想要打印功能,请使用fmt library 或 C++ 格式库(基于上述 fmt 库)。

      即使您出于学习目的决定这样做,我也强烈建议您研究这些替代方案并从它们的做事方式中学习。


      MyAwesomeClass(const char* ptr)
      {
          this->data = ptr;
      }
      
      ~MyAwesomeClass()
      {
          delete data;
      }
      

      哦不,不,不!不!!

      请查看RAII0/3/5 的规则。您的班级要么拥有指向的对象,要么不拥有。如果它拥有它,那么它负责创建和删除它。如果没有,那么它也做不到。你不能有“一半的责任”,你负责删除它而不是创建它。

      在用户代码中,您应该从不需要手动管理内存。使用 0 规则。

      【讨论】:

      • "你不能有“一半的责任”,你负责删除它而不是创建它。" 你可以做到这一点,这很好在很多情况下。 好的是1)从未知来源和所有权的指针隐式转换到拥有该指针的特殊情况(也就是说,当你采用一个对象时,应该有no question 这就是你的意思),以及 2)char const* 的具体用法,它很可能是一个文字字符串,因此不属于你。
      • 感谢@bolov 的参考,我正在修改它。此外,根据这个问题stackoverflow.com/questions/2001286/…,不应该删除 const char 指针,因为它们是在只读区域中分配的(所以它不是“新”运算符或 malloc 的情况)。这意味着我的析构函数有问题(即使到目前为止没有发生崩溃)并且它没有使用“delete[]”语句而只使用“delete”。如果 this->data 是用 new/malloc 操作符创建的,你会接受这个代码吗?
      猜你喜欢
      • 2012-05-19
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 2012-05-19
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      相关资源
      最近更新 更多