每周技巧 #74:委托构造函数和继承构造函数

本节阅读量:

本文翻译自 Abseil 官网的 Tip of the Week #74: Delegating and Inheriting Constructors

原文最初作为 totw/74 发布于 2014 年 4 月 21 日。

作者:Bradley White

“委托工作是有效的,前提是委托者自己也工作。”– Robert Half

当一个类有多个构造函数时,通常需要在每个变体中执行相似的初始化。为了避免代码重复,许多较旧的类会定义一个 private 的 SharedInit() 方法,并从构造函数中调用它。例如:

1
2
3
4
5
6
7
8
9
class C {
 public:
  C(int x, string s) { SharedInit(x, s); }
  explicit C(int x) { SharedInit(x, ""); }
  explicit C(string s) { SharedInit(0, s); }
  C() { SharedInit(0, ""); }
 private:
  void SharedInit(int x, string s) {  }
};

C++11 提供了一种新机制,也就是委托构造函数。它允许一个构造函数基于另一个构造函数来定义,从而更清晰地处理这类情况。如果类中有一些默认初始化代价昂贵的成员,这也能提升效率。

1
2
3
4
5
6
7
class C {
 public:
  C(int x, string s) {  }
  explicit C(int x) : C(x, "") {}
  explicit C(string s) : C(0, s) {}
  C() : C(0, "") {}
};

注意,如果你委托给另一个构造函数,就不能同时使用成员初始化列表;所有初始化都由被委托的构造函数完成。不过不要过度使用。如果共享代码所做的只是设置成员,那么分开的成员初始化列表或类内初始化器,可能比使用委托构造函数更清晰。请运用良好判断。

旁注:在委托构造函数返回之前,对象不被认为是完整的;实践中,这通常只在构造函数可能抛出异常时才重要,因为异常会让发起委托的对象处于不完整状态。

另一种较少见的构造函数代码重复,出现在通过 wrapper 扩展一个多构造函数类的行为时。例如,考虑 C 的一个“薄外衣”子类 D,它只是添加了一个新成员函数。

1
2
3
4
class D : public C {
 public:
  void NewMethod();
};

但 D 的构造函数怎么办?我们希望简单复用 C 的构造函数,而不是写出所有转发样板代码。C++11 通过新的继承构造函数机制允许这样做。

1
2
3
4
5
class D : public C {
 public:
  using C::C;  // 继承 C 的所有构造函数
  void NewMethod();
};

这种用于构造函数的新形式 “using”,与它过去用于成员函数的方式相匹配。

不过要注意,只有当派生类没有新增需要显式初始化的数据成员时,才真正应该继承构造函数。事实上,风格指南也警告不要继承构造函数,除非新成员(如果有)拥有类内初始化。

所以,当 C++11 的委托构造函数和继承构造函数能减少重复、消除转发样板代码,或以其他方式让你的类更简单、更清晰时,请放心使用它们。

每周技巧 #65:把东西放到该放的位置

上一节

每周技巧 #76:使用 `absl::Status`

下一节


本节目录