如何将"Settings"从派生构造函数传递到基构造函数



我有一个类,在构造函数中有一些参数,像这样:

class Foo {
  public:
    Foo(int opt_1=0, int opt_2=100, std::string & opt_3="", bool opt_4=true);
};

然后我说,这有点乱了,我们把这些选项放到Settings类中吧

class Foo {
  public:
    struct Settings {
        int opt_1;
        int opt_2;
        std::string opt_3;
        bool opt_4;
        Settings() : opt_1(0), opt_2(100), opt_3(""), opt_4(true) {}
    }
    
    Foo (const Settings &settings = Settings());
};

的想法是,你设置你的设置,然后构造Foo对象。但是如何从派生类中构造呢?假设我有一个派生类BabyFoo,它具有特定的设置值。之前我可以说:

class BabyFoo : public Foo {
 public:
   BabyFoo() : Foo(0, 500, "babyfoo", false) {}
};

我如何用新的设置类做到这一点?

我想我可以有一个允许设置所有变量的构造函数,但那样就违背了整个目的,不是吗?

免责声明:我不能说OP,有各种各样的可能性取决于实际用例,编码惯例和需求。下面,我将提出我的主观选择,即我认为在这个问题的背景下应该说什么。同样,这个列表并不详尽。

说到这一点,在我看来,这里值得一提的主要可能性是:

  1. 创建一些返回Settings类的工厂方法。
  2. 使用聚合初始化。
  3. 使用构建器模式

您的代码的主要问题是,Settings只有默认构造函数。在这种情况下,你必须在某个地方默认构造它,然后传递它。

#include <string>
class Foo {
  public:
    struct Settings {
        int opt_1;
        int opt_2;
        std::string opt_3;
        bool opt_4;
        Settings() : opt_1(0), opt_2(100), opt_3(""), opt_4(true) {}
    };
    
    Foo (const Settings &settings = Settings());
};
struct BabyFoo : public Foo
{
    static Foo::Settings makeFooSettings()
    {
        Foo::Settings retval;
        retval.opt_1=1;
        retval.opt_2=22;
        retval.opt_3="abcd";
        retval.opt_4=false;
        return retval;
    }
    BabyFoo() : Foo(makeFooSettings()){}
};

如果没有默认构造函数,聚合初始化可以使用:它是否违背了目的?我不知道,你告诉我吧。对于聚合初始化,最后一个参数可以很容易地省略:

#include <string>
using namespace std::literals::string_literals;
class Foo {
  public:
    struct Settings
    {
        int opt_1 {0};
        int opt_2 {100};
        std::string opt_3{"abcd"s};
        bool opt_4{true};
    };
    
    Foo(const  Settings& s);
    Foo();
};
struct BabyFoo : public Foo
{
    BabyFoo() : Foo({8,42}){}
};

(Foo中的两个构造函数由于:使用类内初始化非静态数据成员和嵌套类构造函数时出现错误)顺便说一句,这变得更好了,有点放弃了c++ 20指定初始化式的下一个选项:

struct BabyFoo : public Foo
{
    BabyFoo() : Foo({.opt_4=false}){} //note that this is available since C++20
};

如果你不喜欢这些解决方案,你可以看看builder模式:

#include <string>
using namespace std::literals::string_literals;
class Foo {
  public:
    struct Settings
    {
        int opt_1 {0};
        int opt_2 {100};
        std::string opt_3{"abcd"s};
        bool opt_4{true};
        class Builder
        {
        public:
            Builder& withOpt1(int o) {o1=o; return *this;}
            Builder& withOpt2(int o) {o2=o; return *this;}
            Builder& withOpt3(const std::string& o) {o3=o; return *this;}
            Builder& withOpt1(bool o) {o4=o; return *this;}
            Settings build() const {return Settings{o1,o2,o3,o4};}
        private:
            int o1{};
            int o2{};
            std::string o3{};
            bool o4{};
        };
    };
    
    Foo(const  Settings& s);
    Foo();
};
struct BabyFoo : public Foo
{
    BabyFoo() : Foo(Foo::Settings::Builder().withOpt3("abc").build()){}
};

注意,也许Builder应该直接构建Foo,而不是它的设置,但是,这取决于上下文。

最新更新