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

时间:2012-03-29 15:32:36

标签: c++ inheritance member-variables

所以我搜遍了所有地方,似乎无法找到这个具体问题的答案。我正在使用winXP与cygwin和gcc 3.4.4 cygming special。

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

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。我已经尝试过成员初始化方法,但后来编译器没有实现有关受保护的变量。所以,由于一些奇怪的原因我不知道,它无法在这个类中找到构造函数。即使if包含对受保护成员变量(非继承)的构造函数的“错误”调用,它也会建议正确版本的构造函数。

我还不知道如何做到这一点。非常感谢任何帮助。

5 个答案:

答案 0 :(得分:3)

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

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

首选方法是使用初始化列表来避免不必要的Bar副本。但是,如果这样做,则需要在构造函数外部设置b,上面的示例是如何执行此操作。

答案 1 :(得分:2)

DerFoo的构造函数不能(也不能)初始化b,即Foo作业。 DerFoo的构造函数负责仅初始化DerFoo的直接子对象,即dbFoo,它是DerFoo的基类。反过来,Foo的构造函数负责初始化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);

答案 2 :(得分:1)

我发现这个问题不是很清楚,但让我们看看我是否明白你要做什么以及如何做。

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)将很好,使用member的值初始化i。在初始化之外,语法意味着调用operator()( int )传递i的值。这些成员已经初始化,但是如果要重置它们,则需要分配而不是初始化它们。

答案 3 :(得分:0)

b在Foo类中。访问它(肯定)使用

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

答案 4 :(得分:-1)

您没有拥有来使用初始值设定项列表,此时您肯定应该使用初始值设定项列表。

在构造对象时,在输入构造函数的代码之前,已经构造了所有成员变量。如果您不提供初始化程序,它们将是默认构造的。

此外,在构建变量之后,您无法再次构造变量。您的

this->b(x+3)

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

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

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

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

编辑

由于我错过了上面代码中的第二个doesn't work,因此还有一些其他信息:

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

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

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

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

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