如何在c++中重新初始化基类

how to reinitialize base class in c++

本文关键字:初始化 基类 c++      更新时间:2023-10-16

我有派生类和基类。在派生类的构造函数中,我必须使用基类的基本构造函数。然后,我想用不同的基类构造器重新构造基类:

class A
{
public:
    int a, b;
}
class B : public A
{
    B() : A()
    {
...
    //do some calculations to calculate a and b and then 
   //re-construct class A with the right values.
       A(a,b) <--- ????
    }
}

怎么做呢

构造函数是用来创建对象的。因此它们只使用一次。您应该创建一个方法来进行初始化,并从构造函数调用该方法。

您可以在class A中提供复制和/或移动赋值操作。

class A
{
public:
    int a, b;
    // Copy assignment operator
    A& operator=(const A& rhs) {
        if(this == &rhs) return *this;
        a = rhs.a; b = rhs.b;
        return *this;
    }
    // ...
};

之后,你可以使用模式

重新初始化它
BaseClass::operator=(BaseClass(a,b));

在你的例子中是

A::operator=(A(a,b));

如果你的类是一个聚合,或者有一个隐式定义的复制构造函数,你应该使用它们(你不必自己定义),并使用与上面相同的重新初始化模式。

正如其他人已经指出的,您只能在当前构造函数的初始化列表中调用继承的构造函数,或者(在c++ 11中)委托给当前类的其他构造函数。这是唯一可以初始化类的地方。

<标题> init方法

在某些情况下,添加init()方法是有意义的,它会重新初始化类的部分。这称为两阶段初始化。您可以在一些窗口管理api中找到它。

重要的是要注意,你的对象被分成两个部分:一个是在c'tor中有效初始化的,另一个是在init()中初始化的。您必须(必须,必须!)初始化这两个部分的方式使对象处于一致的状态——in绝不能处于无效状态。作为经验法则:如果对象是(由c'tor)创建的,那么析构函数调用必须始终是可能的。具体来说:不要让指针和句柄到处都是随机值。
class ChildWindow : Compontent {
    shared_ptr<Component> parent_;  // builds the component hierarchy
    Component[]        children_;   // child cp'nts, unknown during c'tor 
    size_t            nchildren_;   // ...use e vector in real code!        
public:
    ChildWindow(chared_ptr<>Component> parent)
      : parent_(parent),
        children(nullptr), nchildren(0) // MUST initialize
    {}
    void addChild(Component *child) { /*... change children_ ...*/ }
    void init() {
        if(nchildren > 0) { /* ...throw away old ... */ }
        children_ = new Component[...];
        // ... init children_ with nulls
    }
};

这只是一个粗略的想法,您可以使用两阶段初始化。

<标题>包装h1> 果你真的只需要重新初始化所有东西,一个技术上的解决方案可能是使用一个简单的包装器类来包装你的真实对象:
class WindowWrapper {
    Window *window_;
public:
    WindowWrapper() : window_(nullptr) {}
    void reset() { delete window_; window_ = nullptr; }
    Window& get() { return *window_; }
    Window& operator*() { return get(); }
}

…随便打出来的,可能有错误。这就是为什么在c++ 11中已经有了这样一个包装器:

unique_ptr<Window> win { new Window{parent, "title"} };
// ..use win...
win.reset( new Window{otherparent, "other title"} };

如果这个unique_ptr不够,你可以把它放在上面的包装。

<标题> 的错误

作为旁注,解释你写的代码的作用:

B::B() : A() { 
   A(a,b); // <--- ????
}

当你输入"????"行时,你创建了一个A类型的*临时对象,它在函数退出时消失。调用当前对象的析构函数。

为什么要创建一个临时对象?你可以写

A a(a,b);

作为语句,您将获得类A的新实例a,该实例由带有两个参数的a c'构造。这个a可以在另一个函数调用中使用,比如func(a);。但是您可以省去显式变量a,只需省略它的名称:

func(A(a,b));

A类的未命名("临时")对象调用func,该对象在语句结束时消失(即;)。

这些临时对象可以创建为表达式。因为每个表达式也是一个语句

A(a,b);

是一个有效的语句——创建一个临时对象,它立即消失。

除非在初始化列表中,否则不能调用父类的构造函数。如果要在不同构造的a对象上使用操作,则必须使用复合而不是继承。如果一个方法改变了一个已经构造的对象,它就不是构造函数。因此,要么用新构造的对象替换对象(而不是更改对象),要么使用非构造函数方法。

将计算a和b的代码放入一个方法中,并在初始化列表中使用它:

class A {
public:
    A(int a, int b): a_(a), b_(b) {}
};
class B : public A
{
public:
    B(): A(B::computeA(), B::computeB()) {}
private:
    static int computeA();
    static int computeB();
};

我将这些方法设置为静态,以防止使用部分初始化的对象。

尽管我不得不说这个问题和例子听起来像你在使用继承来重用实现。在这种情况下,不应该使用继承,而应该使用复合,并用成员对象替换继承。