继承类使用非默认构造函数初始化自定义类

Inherited class initializing a custom made class using non-default constructor

本文关键字:构造函数 初始化 自定义 默认 继承      更新时间:2023-10-16

所以我到处找,似乎找不到这个特定问题的答案。我使用的是带有cygwin和gcc 3.4.4 cygming special的winXP。

问题:我有一个类,它作为一个接口与一些抽象方法和受保护的变量一起工作,这些变量应该在从这个类继承的每个类中。现在我还有另一个类,它是这个接口的成员变量。

class Bar {
private:
    int y;
public:
    Bar(int why);
};
Bar::Bar(int why) : y(why) {}
class Foo {
protected:
    Bar b;
public:
    Foo(int x);
    virtual void print_base();
};
Foo::Foo(int x) : b(x+3)  // Have to use initializer list here.
{
    //this->b(x+3); // doesn't work
}
class DerFoo : public Foo {
protected:
    Bar db;
public:
    DerFoo(int x);
};
DerFoo::DerFoo(int x) : Foo(x), 
    db(x+3) // (Bar)(int) being called, works fine
    // db(4.0, 30) // no matching function for call to Bar::Bar(double, int)
    // note: candidates are Bar::Bar(const Bar&), Bar::Bar(int)
    // b(x-3) // doesn't work class DerFoo does not have any field named 'b'
{
    //this->b(x - 3); //  Doesn't work, error no match for call to (Bar)(int)
    //this->db(x + 3); // Doesn't work, error no match for call to (Bar)(int)
}

所以你可以看到的问题是在派生的foo类内部,DerFoo如何初始化b。我尝试过成员初始化方法,但编译器没有意识到受保护的变量。因此,由于一些我不知道的奇怪原因,它在这个类中找不到构造函数。即使包含对受保护成员变量(非继承)构造函数的"错误"调用,也会建议使用正确版本的构造函数。

我还不知道该怎么做。非常感谢您的帮助。

声明变量后,必须对其进行设置,否则将像函数一样调用它。

this->b = Bar(x+3);

首选的方法是使用初始值设定项列表来避免Bar的不必要副本。但是,如果您确实需要在构造函数之外设置b,上面的例子就是如何做到这一点的。

DerFoo的构造函数不能(也不能)初始化b,即Foo作业。DerFoo的构造函数只负责初始化DerFoo的直接子对象,即db和作为DerFoo基类的FooFoo的构造函数反过来负责初始化b

事件的顺序如下:

  • DerFoo的构造函数调用Foo的构造函数
  • Foo的构造函数调用b的构造函数
  • Foo的构造函数运行其主体,使Foo对象完全构造
  • DerFoo的构造函数调用db的构造函数
  • DerFoo的构造函数运行其主体,使DerFoo对象完全构造完成

如果在DerFoo构造函数中,您不喜欢Foo构造函数在b中留下的值,则可以使用以下任何一种语法为b分配一个新值:

b = Bar(47);
this->b = Bar(47);
this->Foo::b = Bar(47);
Foo::b = Bar(47);

我觉得这个问题不太清楚,但让我们看看我是否理解你想要做什么以及如何做。

DerFoo::DerFoo(int x) : Foo(x), [a]
    db(x+3) 
    // db(4.0,30)          [1]
    // note: candidates are Bar::Bar(const Bar&), Bar::Bar(int)
    // b(x-3)              [2]
{
    //this->b(x - 3);      [3]
    //this->db(x + 3);     [4]
}

第一个错误是[1],编译器告诉您Bar的构造函数不同时使用double和int。该错误还列出了您可以使用的两个可能的构造函数:Bar(int)Bar(Bar const &)。我不确定你想用这条线做什么,但你已经想好了(前一行),只要提供int,电话就可以了。

[2] b不是DerFoo的成员,因此不能在DerFoo的初始化器列表中初始化。Foo负责初始化它自己的成员,这将通过调用[a]中的Foo构造函数来实现。

[3] ,[4],这两个表达式都采用this->member(i)的形式。在初始化期间,语法member(i)将很好地用i的值初始化member。在初始化之外,语法意味着调用operator()( int )传递i的值。这些成员已经初始化,但如果要重置它们,则需要分配,而不是初始化它们。

b在Foo类中。要访问它(当然),请使用

Foo::b = Bar(x-3);

您不需要使用初始值设定项列表,在这一点上,您肯定也应该使用初始化项列表。

在构造对象时,在输入构造函数的代码之前,所有成员变量都已构造完成。如果不提供初始值设定项,它们将是默认构造的。

此外,在构造变量之后,不能再构造该变量。您的

this->b(x+3)

不是告诉编译器构造b,而是告诉它在对象上调用一个名为b的函数。这样的函数在您的类中不存在,因此出现了错误。请注意,一旦将对象构造为变量,就无法再次调用该变量的构造函数(只是为了更改值)。

与大多数语言一样,可以使用=更改值。因此你可以做:

Foo::Foo(int x)
{
   this->b = Bar(x+3);
}

这意味着您正在创建另一个无名称的Bar对象,并将其值指定给this->b。您应该知道,这意味着,在创建Foo时,您将创建两个Bar对象。首先是默认构造的,之前。输入构造函数代码,然后输入新的无名称代码。然后,您将最终将值分配给已经构建的对象,因此此代码比使用初始值设定项列表的代码效率低得多。

编辑

由于我错过了上面代码中的第二个doesn't work,这里有一些附加信息:

您还试图在派生的DerFoo对象的构造函数中直接初始化b。然而,一旦达到代码的这一部分,就已经构建好了。因此,任何在派生构造函数中构造它的尝试都会迟到。

因此,您必须向Foo添加另一个构造函数,该构造函数接受该值,并在DerFoo的构造函数中使用该值。该解决方案是优选的,因为它将只在b中构造Bar对象一次。如果不能添加这样的构造函数,则需要在DerFoo的构造函数代码中使用赋值。

即使使用了作用域运算符,尝试直接在DerFoo构造函数中初始化b也不会起作用。

DerFoo::DerFoo() : Foo::b(x-3) {}

仍然会产生错误:http://ideone.com/6H8ZD