【问题标题】:Init var without copy constructor没有复制构造函数的 init var
【发布时间】:2011-02-25 11:36:30
【问题描述】:

我有一些没有复制构造函数的类(Window)(它是私有的)。我不明白如何在我自己的类中初始化这个类的 var:

class MyClass
{
   Window obj; // Hasn't copy constructor
   public:
      void init()
      {
         obj = Window(/* constructor params */); // [error]
         obj(/* constructor params */); // [error]
      }
}

错误 1initializing argument 1 of ‘Window::Window(WindowHandle, const sf::WindowSettings&)’

错误 2‘NonCopyable& NonCopyable::operator=(const NonCopyable&)’ is private

但它是这样工作的:

Window obj(/* constructor params */);

【问题讨论】:

  • 你的类使用init()成员函数而不是使用构造函数有什么原因吗?
  • @sbi,是的,有。不要问为什么=)
  • 我重新表述:您使用init() 成员有好的理由吗? (一个很好的理由是,例如,如果初始化需要调用虚函数,则需要两步构造。)

标签: c++ constructor copy init


【解决方案1】:

使用initializer list

class MyClass
{
   Window obj; // Hasn't copy constructor
   public:
      MyClass() :
         obj(/* constructor params */)
      {
      }
}

这也适用于参考。您可以在初始值设定项列表中分配任何成员变量。不过,它只适用于构造函数。

如果你想让它在构造函数之外工作,你需要使用指针:

class MyClass
{
   Window *obj;
   public:
      void init()
      {
         obj = new Window(/* constructor params */);
      }
}

请务必在您的解构器中使用delete 释放obj(并在必要时将解构器设为虚拟)。

【讨论】:

  • 只有构造函数可以有初始化列表。
  • @计算机,真;我忽略了这一点。对不起。
  • 如果要使用堆,请确保obj 得到妥善管理。请参阅我的答案以了解如何做到这一点。
  • @In silico,D'oh,忘记了。好久没用C++了。 xD
【解决方案2】:

您的MyClass 需要一个构造函数来初始化obj 成员。

class MyClass 
{ 
private:
    Window obj;
public: 
    MyClass() : obj(/* constructor params */) // This is an initializer list
    {}
};

如果您需要init() 函数,而Window 对象提供了自己的 init() 某种功能,你可以这样做:

class MyClass 
{ 
private:
    Window obj;
public: 
    void init() 
    { 
        obj.init(/* init params */); // Window's own init() function
    }
};

如果Window 类没有类似init() 的函数,您可以使用堆(除非绝对必须,否则不推荐):

class MyClass 
{ 
private:
    // Alternatively, we can use a smart pointer here and avoid
    // managing memory ourselves.
    Window* obj;
public: 
    MyClass() : obj(0) {}
    ~MyClass() { uninit(); }
    void init() 
    { 
        uninit();
        obj = new Window(/* constructor params */);
    }
    void uninit()
    {
        if(obj != 0)
        {
            delete obj;
            obj = 0;
        } 
    }
};

如果Window 类声明了私有复制构造函数和/或复制赋值运算符,则您不能将新的Window 实例分配给obj

【讨论】:

  • 删除空指针是safe,所以不需要在uninit函数中检查if(obj != 0)
  • 在 NULL 指针上调用 delete 是安全的,当且仅当它是默认的全局删除并且全局删除没有错误。我编写的可移植代码需要在不同的构建和执行环境下保持稳健,因此我总是在删除之前检查 NULL。
  • 标准明确规定delete 在使用空指针调用时应该正确运行。我会远离并非如此的环境,如果他们未能满足如此简单的要求,我非常害怕我可能会遇到什么其他惊喜。
  • 另外,堆分配不是必需的(尽管我猜是第一个反射),请查看我的答案以获得详细的解决方法。
【解决方案3】:

如果你的复制构造函数是私有的,那么类确实有一个复制构造函数。看来您的班级将复制 ctor 和分配操作都设置为私有,这解释了第二条错误消息。第一条错误消息与 WindowHandle 类有关,您没有显示。

为了更清楚地理解这一点,我们还需要查看 Window 类 - 它(例如)是否有默认构造函数?

【讨论】:

  • @Neil:Window 不需要默认可构造。我们需要的唯一要求是它的析构函数不要抛出(这当然只是很好的意义)。之后有内存对齐问题等......但是,我们有相应的库;)
【解决方案4】:

我想我会为此彻底废掉(在发怒之前读到最后)但是......假设 windows 的构造函数从不抛出:

void MyClass::init()
{
  obj::~Window();          // destroy the object
  new (&obj) Window(...);  // construct the object
};

我当然要强调构造函数的 not throw 要求,如果它抛出你会遇到一个非常混乱的情况:MyClass 的析构函数将调用 @987654325 的析构函数@ 无论对象是否由于构造失败而处于活动状态、被踢或被丢弃,在后一种情况下,您会得到未定义的行为。

当然,一个典型的事情就是std::unique_ptr<Window>,但是我们遇到了动态分配的障碍,显然情况并不强制...

所以你最好使用库:Boost.Optional

class MyClass
{
public:

private:
  boost::optional<Window> obj;
};

语法调用类似于指针:

obj->foo();

但一个好处是您可以使用更安全的语义获得就地破坏/构造。破坏很容易:

// both call ~Window() if an instance had been constructed
obj.reset();
obj = detail::none_t();

对于构造,请使用TypedInPlaceFactory。对于分配也是...当然首先清除先前的实例(如果有的话):

void MyClass::init(Arg1 arg1, Arg2 arg2)
{
  obj = boost::in_place<Window>(arg1, arg2);
}

主要优点是,如果现在在构造过程中遇到任何异常,optional 对象将处于unitialized 状态,这是完全可行的,因此您不必担心未定义的行为。

所以基本上它就像拥有一个由智能指针拥有的动态分配的对象...除了神奇的是对象不是动态分配的,因此保证了与“普通”对象相同的性​​能:)

我应该补充一点,不可复制的障碍是 InPlaceFactory 创建背后的原因之一。

【讨论】:

    【解决方案5】:

    重点是不让你克隆它。

    像这样初始化:Window obj(其他构造函数的参数,不是复制的)

    窗口 &obj = somefunctionConstructingSuchAWindow();

    【讨论】:

      【解决方案6】:

      如果Window 没有复制构造函数,则不能将Window 类的另一个对象分配给它。您只能使用初始化列表从MyClass 的构造函数初始化obj。例如:

      class MyClass
      {
         Window obj; // Hasn't copy constructor
         public:
            MyClass()
                : obj(/*constructor params*/)
            {
                /*...*/
            }
      }
      

      【讨论】:

        【解决方案7】:

        类成员的初始化应该在类构造函数中完成,如下例所示:

        class MyClass
        {
        public:
           MyClass(/* constructor params */);
        
        private:
           Window m_obj; // Hasn't copy constructor
        };
        
        MyClass::MyClass(/* constructor params */) : m_obj(/* constructor params */)
        {
        }
        

        【讨论】:

          猜你喜欢
          • 2012-07-07
          • 1970-01-01
          • 1970-01-01
          • 2013-05-26
          • 1970-01-01
          • 2015-07-24
          • 2011-07-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多