在2018年使用C++ 11及更高版本时,helper init()函数被认为是不好的形式吗?

cod*_*ode 9 c++ c++11

在C++ 11之前,您没有非静态成员初始化,也没有构造委派,因此人们经常使用私有帮助函数来帮助初始化以减少代码复制.

这是2018年的好代码吗?

class A  {
  int a1 = 0;
  double a2 = 0.0;
  string a3 = "";
  unique_ptr<DatabaseHandle> upDBHandle;

  void init(){
      upDBHandle = open_database(a1, a2, a3);
  }

public:
    A() { init(); }
    explicit A(int i):a1(i) {  init(); }
    explicit A(double d):a2(d) {  init(); }
    explicit A(std::string s):a3(std::move(s)) {  init(); } 
    A(int i, double d, std::string s) : a1(i), a2(d), a3(std::move(s)) { init(); }
};
Run Code Online (Sandbox Code Playgroud)

如何改进此代码?

Joh*_*itb 5

在我看来,你的代码很好。我尽量避免依赖微妙的效果,例如构造函数初始化列表中的成员初始化顺序。它违反了 DRY - 您需要重复使用相同的顺序:在声明成员时在类体中,以及在构造函数初始化列表中。随着时间的流逝,类变得更大,并且您将构造函数移到.cpp文件中,事情开始变得更加混乱。因此,我将需要访问其他成员的东西放入init函数中。

如果成员是const,则不能执行此操作。但话又说回来,作为类作者,您可以决定哪个成员是 const,哪个不是。请注意,这不要与“构造,然后初始化”的反模式混淆,因为这里init发生在构造函数中,而这对类用户是不可见的。

如果您仍然不喜欢使用init,我建议不要将调用放入构造函数初始化列表中。也许对我来说,一个可接受的中途是将它放入类内初始化程序中,并从构造函数中删除所有调用。

class A  {
  int a1 = 0;
  double a2 = 0.0;
  string a3 = "";
  unique_ptr<DatabaseHandle> upDBHandle = open_database(a1, a2, a3);

  // ...
Run Code Online (Sandbox Code Playgroud)

  • +1 自动告诉 OP 他的代码很好;我为 OP 感到难过,因为最高票数告诉他他的代码很糟糕,尽管在 C++ 中提供更好的解决方案并不容易!可能还值得一提的是 -Wreorder 警告,大多数(所有?)主要编译器都支持;对于不知道它的人来说,这个标志对于避免你提到的一些微妙之处有很大帮助。 (2认同)

Nir*_*man 3

C++ 不太擅长处理多个默认值。因此,要做好这件事并不容易。您可以执行多种操作,但所有操作都有不同的权衡(例如,分散默认值)。

恕我直言,这里可以找到的最好的解决方案,虽然还不是合法的 C++,但它是一个高度支持的扩展:指定的初始值设定项。

class A  {
  struct Params {
      int a1 = 0;
      double a2 = 0.0;
      string a3 = "";
  };
  Params p;
  unique_ptr<DatabaseHandle> upDBHandle;

public:
    explicit A(Params p_arg) 
      : p(std::move(p_arg))
      , upDBHandle(open_database(p.a1, p.a2, p.a3) { }
};

A a({});  // uses all defaults
A a2({.a2 = 0.5});  // specifies a2 but leaves a1 and a3 at default
A a3({.a1 = 2, .a2=3.5, .a3 = "hello"});  //  specify all
Run Code Online (Sandbox Code Playgroud)