类继承调用不同的构造函数

class inheritance call a different constructor

本文关键字:构造函数 继承 调用      更新时间:2023-10-16

hei我有一个c++03类,它有一个接受整数的简单构造函数。以及一个具有序列化方法的派生类,该类应将文件名作为构造函数,从中加载整数,然后调用第一个构造函数。

class A {
public:
    A(int foo);
}

和一个派生类:

class XmlableA : public A {
public:
    XmlableA(int foo);
    XmlableA(string xmlfilename) {
        //load foo from xml
        // call A::A(foo)
    }
}

我尝试了一些不同的解决方案,但每次我得到

no matching function for call to ‘A::A()’

几乎所有的答案都是一样的,所以我建议一个不同的解决方案,这是我个人更喜欢的。

static成员函数Create定义为:

class XmlableA : public A {
public:
    XmlableA(int foo);
    //static member function
    static XmlableA Create(string const & xmlfilename) 
    {
        //load foo from xml
         int foo = /*load from file*/;
         return XmlableA(foo);
    }
};

用法:

XmlableA xmlable = XmlableA::Create(xmlFile);

初始化它,如下所示:

XmlableA(int foo) : A(foo) {}

您也可以考虑:

private:
  static int LoadXML(const string& xmlfilename) {
    int ret = ...; << load here
    return ret;
  }
public:
  XmlableA(string xmlfilename) : A(LoadXML(xmlfilename)) {
  }

在C++中,基类是在Child类之前构造的,因此您将无法执行此操作。您可以创建一个Factory,该Factory采用文件名并根据该文件中的内容创建对象。

示例:

class XmltableAFactory {
public:
    static XmltableAFactory build(string xmlfilename) {
        // read int foo from xmlfilename
        return XmltableAFactory(foo);
    }
};

然后这样称呼它:

XmltableA myObj = XmltableAFactory::build(filename);

有几点需要注意。

  1. 这意味着您将不需要XmltableA类中的string xmlfilename协构造函数,因为如上所述,在调用基类的构造函数之前,您无法知道foo
  2. 您可以选择通过值或通过指针从工厂返回。编译器可能会按值优化返回,因为您正在创建对象并在同一行返回它。然而,众所周知,通过指针返回通常会更快,但您必须创建一个new对象,然后确保在使用完delete对象后将其返回
  3. 如果你不想浪费内存,可以看看boost的auto_ptrshared_ptr

如果你想在调用A::A(int)之前做点什么,你最终不得不破解,比如

int XmlableA::f(string filename) { /* load foo from xml */; return foo; }
XmlableA(string xmlfilename) : A(f(filename)) {}

好的,所以第一个很容易:

XmlableA::XmlableA(int foo) : A(foo)
{
}

第二个需要做类似的事情

XmlableA(string xmlfilename) : A(fooFromXML(xmlfilename))
{
}

我们可以实现为

class XmlableA : public A
{
    static int fooFromXML(string filename);
public:
    // ...

注意,加载XML文件并返回所需整数的fooFromXML必须是静态的,因为当我们调用它时,我们还没有XmlableA实例来调用它。


对于多个参数(以及一般设计),工厂可能是最好的:如果你执着于构造函数模型,不关心效率,你可以这样做:

class XmlableA : public A
{
    static int intFromXML(char const *varname, string const &filename);
public:
    XmlableA(string const &xmlfilename)
    : A(intFromXML("foo", xmlfilename), intFromXML("bar", xmlfilename))
    {
    }

如果您关心重复解析XML文件,而不关心可重入性,则可以通过将xFromXML缓存在静态成员中来"记忆"它。

如果类A没有默认构造函数,则必须显式调用派生类的初始化列表中的构造函数。XmlableA(string fn) : A(readIntegerFromFile(fn)) {}

但是,您应该考虑将序列化"外包"到一个单独的类中。例如,如果您有一个类型为A的对象,现在您想序列化它,会发生什么?您不能,因为您只能序列化XmlableA。此外,如果您的客户决定不再需要XML序列化,而是使用Yaml或某些专有格式,会发生什么情况?您将不得不更改所有代码。