【问题标题】:C++ design pattern: multiple ways to load fileC++ 设计模式:加载文件的多种方式
【发布时间】:2012-04-07 17:26:53
【问题描述】:

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

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

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

这听起来像是一个经典问题,但我无法找到解决方法(我最近使用 Python 进行编程,我相信由于弱类型,它总是会调用预期的函数)。

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

你能帮忙吗?

输出:

使用构造函数加载:
Base::加载文件_A
Base::load file_B 加载带函数后期构造:
Derived_A::加载文件_A
Derived_B::加载文件_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;
}

【问题讨论】:

    标签: c++ oop design-patterns


    【解决方案1】:

    您看到的行为在 C++ 中定义良好——它在这种情况下没有用处,因为当您从 Base::Base(std::string) 调用 load(std::string) 时,该类没有完全构造。

    有两种直接的方法:

    一个

    您可以使用调用 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++,您当然可以选择。

    【讨论】:

    • 我最终使用了loader 模式。非常感谢!
    【解决方案2】:

    您可以查找"Named Constructor Idiom"

    【讨论】:

    • +1 好点,这就是 python 所鼓励的。你知道我是否需要为每个子类编写命名构造函数吗?或者有没有办法编写一次以防止重复代码(它总是调用load,然后可能还有其他一些函数)。
    • 您无权访问需要文件(尝试打开链接时出错)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2016-02-28
    • 1970-01-01
    • 2012-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多