是否可以在基类构造函数之前运行成员初始值设定项
Is it possible to run member initializers before base-class constructor?
通常可以通过更改成员在类中声明的顺序来更改成员初始化程序的运行顺序。但是,有没有办法让基类初始值设定项/构造函数不首先运行?
这是我的问题的一个最简单的草图:
class SpecialA : public A {
public:
explicit SpecialA(Arg* arg)
: member(expensiveFunction(arg))
, A(member) // <-- This will run first but I don't want it to
{}
private:
T member;
}
不,这是不可能的。类初始化总是这样:基类、成员、此类构造函数。
原因很简单——因为你可以在这个类构造函数中引用你的成员,所以你必须在调用构造函数之前构造成员。由于您可以从成员中引用基类成员,因此必须在此类成员之前构造它们。
是否可以在基类构造函数之前运行成员初始化程序?
并非如此。
也就是说,这里有几个解决方案:
1:将成员移动到人造基地:
template<typename T>
class InitializerBase {
protected:
InitializerBase(T&& value) : member(std::move(value)) {}
T member;
};
class SpecialA: public InitializerBase<T>, public A {
public:
SpecialA(Arg *arg) : InitializerBase<T>(expensiveFunction(arg)): A{}
{
}
};
(可能)最好的解决方案是这样的:
2:对完全构建的值使用依赖注入(这是最安全、最好的设计,除非你的代码有更大的问题,否则是最有效的实现):
class SpecialA : public A {
public:
explicit SpecialA(T fully_computed_value)
: A(fully_computed_value)
, member(std::move(fully_computed_value)) // no heavy computations performed
{}
private:
T member;
}
结构:
auto &a = SpecialA(expensiveFunction(arg));
这是最好的解决方案,因为如果expensiveFunction
抛出,您甚至不需要开始构建结果对象(应用程序不必释放半构建的SpecialA的资源)。
编辑:也就是说,如果你想隐藏昂贵函数的使用,标准的解决方案(标准的,因为它是可重复使用的,极简主义的,并且仍然尊重良好的设计)是添加一个工厂函数:
SpecialA make_special(Arg *arg)
{
auto result = expensiveFunction(arg);
// extra steps for creation should go here (validation of the result for example)
return SpecialA( std::move(result) );
}
客户端代码:
auto specialA = make_special(arg); // simplistic to call (almost as simple
// as calling the constructor directly);
// hides the use of the expensive function
// and provides max. reusability (if you
// need to construct a SpecialA without
// calling the expensive function, you
// can (by obtaining the constructor argument
// for SpecialA in a different way)
刚刚意识到如何解决问题:
class SpecialA : public A {
public:
explicit SpecialA(Arg* arg) : SpecialA(arg, expensiveFunction(arg)) {}
private:
SpecialA(Arg* arg, T&& member) : A(member) , member(member) {};
T member;
}
必须首先调用基类构造函数。你不能采取任何其他方法,但你可以设置一种方法,不使类成为基类,并首先调用你想要的任何东西。
基类总是首先完全构造。这是没有办法的。
一种选择是打破继承,将基类移动到子类的成员变量。
成员初始化的顺序就是它们在类声明中出现的顺序。
但是依赖它会使代码变得脆弱,所以请记住这一点。在类声明中放一个合适的注释可能不足以阻止热情的重构者。
列表中成员初始值设定项的顺序无关紧要:实际初始化顺序如下:
1) 如果构造函数用于派生最多的类,虚拟基类在它们在深度中出现的顺序先从左到右遍历基类声明(从左到右指的是基本说明符列表)
2) 然后,直接基类在中初始化从左到右的顺序,因为它们出现在这个类的基本说明符列表中
3) 然后,按以下顺序初始化非静态数据成员类定义中的声明。
4) 最后构造函数执行
来源:http://en.cppreference.com/w/cpp/language/initializer_list
在您的情况下,反转项目的所有权看起来更正确:
struct A
{
protected:
A(T expensive_object) : _expensive_object(std::move(expensive_object)) {}
protected:
T& access_expensive_object() {
return _expensive_object;
}
private:
T _expensive_object;
};
class SpecialA : public A {
public:
explicit SpecialA(Arg* arg)
: A(expensiveFunction(arg))
{}
void use_object() {
auto& o = expensive_object();
do_something_with(o);
}
};
您还可以使用保留的可选参数作为占位符:
class SpecialA : public A {
public:
SpecialA(Arg* arg, std::optional<T> reserved = {}) :
A(reserved = expensiveFunction(arg)),
member(reserved)
{}
private:
T member;
}
它可能比委托构造函数更好,因为当有很多参数时,它会减少代码的膨胀。此外,委托构造函数可能会有更多的开销(如果我错了,请纠正我)。
- 在运行时有条件地删除类成员或跳过调用该成员对象的构造函数
- 在运行时选择类成员的类型
- 运行时多态性 - 箭头运算符访问了错误的成员?
- 在C++中使用链表的堆栈实现中,访问结构体headNode成员count和top会导致运行时错误
- 在运行时为随机分布类成员设置最小和最大边界?
- 在VS2015中访问类成员时运行时错误,但在Linux上未访问时出错
- 参数包推导不一致 int 和 int& 在可变参数模板化成员函数中创建运行成员函数的线程
- 在另一个 QThread 上运行成员方法时,无法将事件发送到其他线程拥有的对象
- 未知大小的数组作为类成员,用于在运行时(对象创建时间)创建数组的对象
- 未经授权的私有类成员访问会产生编译时错误而不是运行时错误?
- 如何子类可运行任何成员功能
- 如何在运行时使用静态成员函数初始化静态成员变量
- 由于固定尺寸成员而引起的EIGEN运行时断言
- 从成员变量更新类变量或调用类功能是给出运行时错误
- 有没有办法检测当前成员函数是在左值还是右值上运行?
- 移动包含正在运行的 std::thread 成员的对象
- 如何在Google测试(gtest)中使用fixture成员值运行参数化测试
- 在单独的线程中运行成员功能
- 如果其对象在多个线程中运行,我们是否需要锁定类成员功能
- 如何在组合中在运行时更改成员对象?