【问题标题】:Copying a C++ class with a member variable of reference type使用引用类型的成员变量复制 C++ 类
【发布时间】:2010-10-01 16:47:37
【问题描述】:

我有一个类,它存储对其父级的引用,该引用在构造函数中传递。如果我尝试复制一个实例,我会收到一个错误“错误 C2582:'operator =' 函数不可用”,可能是因为引用不可分配。

有没有办法解决这个问题,或者我只是将变量更改为指针而不是引用?

例如(过于简单但我认为有重点):

class MyClass
{
public:
 MyClass(OtherClass &parent) : parent(parent) {}
private:
 OtherClass &parent;
};

MyClass obj(*this);
.
.
.
obj = MyClass(*this);

【问题讨论】:

  • 可能编译器混淆了相同的参数和成员名称?
  • 引用总是不变的。一旦你给它赋值(在你的构造函数中),就不可能改变它的值。所以,如果你想支持做作,你把它改成一个指针。

标签: c++


【解决方案1】:

有一种方法可以做到这一点并且仍然使用参考,使用reference_wrapper。所以

    T& member;

变成

    std::reference_wrapper<T> member;

引用包装器基本上只是可​​重新分配的引用。

【讨论】:

  • 如果 C++11 及更高版本可用,这似乎是最好的方法。
  • 这应该是现在公认的答案。以最易读的方式解决了我所有的问题。
  • 非常感谢!仅这个答案就教会了我很多 C++!现在一切都说得通了。
  • 这比指针更可取,因为您清楚地传达了member-value 不能为空。如果它是一个指针,其他人总是要问自己指针是否为空是否有效
【解决方案2】:

我完全不推荐这个

但如果你真的很想这样做:

#include <new>

MyClass::MyClass(const MyClass &rhs): parent(rhs.parent)
{
}

MyClass &MyClass::operator=(const MyClass &rhs)
{
    if (this!=&rhs)
    {
        this->~MyClass();
        new (this) MyClass(rhs);
    }

    return *this;
}

【讨论】:

  • 警告:这种方法是未定义的行为。 “如果,......在原始对象占用的存储位置创建了一个新对象,......将自动引用新对象,并且一旦新对象的生命周期开始,就可以用来操作新对象对象,如果: ... 原始对象的类型不是 const 限定的,并且,如果是类类型,不包含任何类型为 const- 的非静态数据成员限定或引用类型 ..."
【解决方案3】:

是的,如果您需要支持赋值,那么将其设为指针而不是引用几乎是您唯一的选择。

【讨论】:

    【解决方案4】:

    是的,只需将成员设为指针即可。引用将无法重新安装,并且没有解决方法

    编辑: @"Steve Jessop" 有效地指出了如何使用 PIMPL 成语(使用“d-pointer”的私有实现)解决问题。在分配中,您将删除旧的实现并创建一个从源对象的 d 指针复制构造的新实现。

    【讨论】:

    • “没有变通办法”。为此,我将提到错误实现operator= 以显式调用析构函数的可能性,然后使用placement new 和复制构造函数进行构造。或者更有效的是,使用 pimpl 并让 operator= 进行复制和交换。 impl 本身不需要operator=,仍然可以使用引用。尽管如此,如果这是 pimpl 所需的唯一功能,那么要编写很多样板......
    • @Steve Jessop:抱歉,请您再解释一下。我不确定我是否了解解决方法
    • @Chubsdad 的想法是你的类将像 class MyClass { class Pimpl; Pimpl *p; public: /* dtor and ctors ... */ void swap(MyClass &amp;m); MyClass(MyClass const&amp;m); MyClass &amp;operator=(MyClass m); }; 和 C++ 文件中的 class MyClass::Pimpl { Parent &amp;parent; }; MyClass::MyClass(MyClass const&amp; m):p(new Pimpl(*m.p)) { } MyClass &amp;operator=(MyClass m) { swap(m); return *this; } void MyClass::swap(MyClass &amp;m) { std::swap(p, m.p); }。这在某种程度上可以被视为一种“变通”,我怀疑:)
    • 是的,他说的是,但有更多的换行符 ;-)。正如我所说,为了避免指针,它有很多样板,并且在反射 litb 上是正确的:如果它是一种解决方法,那么它是一个很长的路要走。
    • 但是'new Pimpl(*m.p)' 不是将原始副本重新介绍给我们吗?我认为这种方法只是将问题从 MyClass 转移到 Pimpl,而没有在任何地方实际解决它。不知道我是否仍然得到它。
    【解决方案5】:

    您需要实现一个复制构造函数并在该复制构造函数中初始化引用,以指向与原始对象相同的引用。

    【讨论】:

      【解决方案6】:

      我会将其设为 boost::shared_ptr。你可以对这些很粗暴,他们会照顾好自己。而使用原始指针意味着您必须担心该对象保持活动状态

      【讨论】:

      • 这与使用引用没有区别...您必须相信所引用的对象仍然存在,无论是使用引用还是原始指针。
      • 非常不同。只要有人指向它, boost shared_ptr 就会使对象保持活动状态,原始指针或引用不会这样做
      • 问题是这通常会陷入引用循环(子指向父,父指向子),这将导致引用计数永远不会下降到0并且对象当没有其他对它们的引用时泄漏。您可以通过将其中一个指针设为weak_ptr 来避免该问题,但是您必须在使用时检查它。
      【解决方案7】:

      正如其他人所提到的,可以使用 std::reference_wrapper 。也可以使用辅助函数 std::ref() 和 std::cref()。与其他帖子不同的是,C++03 在命名空间 std::tr1 中引入了 reference_wrapper、ref() 和 cref(),因此如果您不使用 C++11 或更高版本,您可以选择。

      【讨论】:

        猜你喜欢
        • 2012-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-30
        • 1970-01-01
        相关资源
        最近更新 更多