C++ 设计模式:多种加载文件的方法

3

摘要:寻找通过构造函数加载不同文件的标准C++设计模式

我有一个Base类,其中包含所有派生类(例如Derived_ADerived_B)将使用的一些功能。主要区别在于Derived_ADerived_B覆盖了load函数,该函数由构造函数用于加载数据文件(load也可以在构造函数外显式调用)。

我遇到了一个意想不到的问题:构造函数调用的load函数将类视为Base类型,但是当我使用默认构造函数并显式调用load函数时,虚拟函数表允许调用预期的load函数。

这似乎是一个经典问题,但我找不到解决方法(我最近正在使用Python进行编程,因为弱类型,所以我认为它总是会调用预期的函数)。

同样地,我真的希望Base::load是纯虚/抽象的(只有派生类将被实例化);但是,这样无法编译(我相信这是因为编译器看到将调用纯虚函数)。

你能帮忙吗?

输出:

使用构造函数加载:
Base::load file_A
Base::load file_B 使用后构造函数加载:
Derived_A::load file_A
Derived_B::load file_B

代码:

#include <iostream>
#include <string>

class Base
{
public:
  Base() {}
  Base(std::string x)
  {
    load(x);
  }
  virtual void load(std::string x)
  {
    std::cout << "\tBase::load " << x << std::endl;
  }
};

class Derived_A : public Base
{
public:
  Derived_A() {}
  Derived_A(std::string x): Base(x) {}
  void virtual load(std::string x)
  {
    std::cout << "\tDerived_A::load " << x << std::endl;
  }
};

class Derived_B : public Base
{
public:
  Derived_B() {}
  Derived_B(std::string x): Base(x) {}
  void virtual load(std::string x)
  {
    std::cout << "\tDerived_B::load " << x << std::endl;
  }
};

int main()
{
  // simpler code, but it doesn't behave as I hoped
  std::cout << "Loading w/ constructor:" << std::endl;
  Base*der_a = new Derived_A(std::string("file_A"));
  Base*der_b = new Derived_B(std::string("file_B"));

  // this is what I want to do
  std::cout << "Loading w/ function post construction:" << std::endl;
  der_a = new Derived_A;
  der_a->load( std::string("file_A") );
  der_b = new Derived_B;
  der_b->load( std::string("file_B") );
  return 0;
}
2个回答

2
您看到的行为在C++中是很明确定义的 - 只是在这种情况下不太有用,因为当您从Base::Base(std::string)调用load(std::string)时,类还没有完全构造好。
有两种立即可行的方法: 方法A 您可以使用容器类型来调用load(可能也保留字符串)。如果需要保留实例(例如,它们可能具有特定的错误信息),则这可能更实用。
class Loader 
{
public:
    Loader(Base* const p, const std::string& location) : d_base(p) 
    {
        this->d_base->load(location);
    }

private:
    std::unique_ptr<Base>d_base;
private:
    Loader(const Loader&) = delete;
    Loader& operator=(const Loader&) = delete;  
};

正在使用:

std::cout << "Loading w/ Loader:\n";
Loader l_der_a(new Derived_A, "file_A");
Loader l_der_b(new Derived_B, "file_B");

B

你也可以使用一个帮助函数来解决问题:

class Base {
public:
    template<typename T>
    static void Load(const std::string& x) 
    {
         T().load(x);
    }

    Base() 
    {
    }

    Base(std::string x) 
    {
         /* load(x); << see Load(const std::string&) */
    }

    virtual ~Base() 
    {
    }

    virtual void load(std::string x) = 0;
};

使用中:

std::cout << "Loading w/ Base::Load<T>():\n";
Derived_A::Load<Derived_A>("file_A");
Derived_B::Load<Derived_B>("file_B");

除此之外,还有其他几种方法和变体-具体取决于哪种最适合你的设计。 使用C ++,你肯定有选择。


1
我最终使用了“loader”模式。非常感谢! - user

2
你可以查看“具名构造函数惯用法”。(链接)

+1 很好的观点,这正是Python所鼓励的。你知道我是否需要为每个子类编写命名构造函数吗?还是有一种方法可以只编写一次以防止重复代码(它总是调用load,然后可能是其他一些函数)。 - user
1
您没有访问所需文件的权限(尝试打开链接时出现错误)。 - CoffeDeveloper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接