调用派生 class 的重写虚拟方法而不公开它们
Invoke overridden virtual methods of a derived class without exposing them
我想编写一个函数 f
,它以特定顺序调用派生 class 的两个重写虚拟方法 op_1
和 op_2
,无需公开那些超出 f
的方法和包含 classes.
的方法
我能想到一些有效或几乎有效的方法,但我也是 c++ 的新手,希望得到一些帮助,以在约定性、可维护性等方面选择“最佳”方法:
- 使
f
成为基础 class (Base
) 的 public 非虚拟方法,它按顺序调用虚拟操作,并将操作设为私有。
/***** .h *****/
class Base {
virtual void op_1() const = 0;
virtual void op_2() const = 0;
public:
void f();
};
/***** .cpp *****/
void Base::f() {
op_1();
op_2();
}
- 制作 ops public 并公开一个非成员非好友助手以按顺序调用它们。
/***** .h *****/
struct Base {
virtual void op_1() const = 0;
virtual void op_2() const = 0;
};
void f(const Base& b,) {
b.op_1();
b.op_2();
}
- 将 ops 保留为私有并公开一个 friend 函数以按顺序调用它们。
/***** .h *****/
class Base {
virtual void op_1() const = 0;
virtual void op_2() const = 0;
friend void f(const Base& b);
};
void f() {
b.op_1();
b.op_2();
}
#1 看起来很糟糕,因为派生的 classes 也可以访问 f
,但它对他们没有用处或用处,可能会被滥用。
#2 解决了这个问题,但封装较少,因为操作是 public.
#3 似乎解决了这两个问题,但我是 C++ 的新手并且对交友事物持冷漠态度——它似乎混淆了接口,我读过的指南(例如 Effective C++ #23)不鼓励在没有明确的情况下使用它需要。这是一个合适的案例吗?
#4 ???可能有更好的方法我看不到。开导我!
特定于应用程序的上下文,以防您需要“为什么”
我需要在应用程序初始化时向第三方数据存储注册一些数据类型 - 我需要按顺序执行 2 个同步操作:
store.data_type<MyAppDataType>()
将应用特定数据类型的 table 添加到商店。
store.data_type<MyAppDataType>().member<MemberDataType>("member_name")
生成在步骤 1 中注册的特定于应用程序的数据类型,并设置其字段之一以与库的反射 API 一起使用 - 对于在非生产版本中进行调试非常有用。
操作 #2 是可选的,但如果执行它应该在操作 #1 之后发生。
我希望我的应用程序的各个模块[1] 将它们自己的数据注册到商店,以避免出现一个大而混乱的文件,其中应用程序的所有数据都注册在一起。为此,我在每个模块中包含一个 class,它扩展了一个 DataRegistrar
基础 class - 派生的基础力量 classes 为两者提供特定于模块的实现以上操作:
struct DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
};
在应用程序的其他地方,我可以有一些在应用程序启动时运行的代码,这些代码按顺序为每个模块调用 register_data_types
和 setup_reflection
。以下是在简化问题中映射到上述方法的方法:
- (映射到上面的#1)
/***** .h *****/
class DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
public:
void register_data(lib::store& store);
};
/***** .cpp *****/
void DataRegistrar::register_data(lib::store& store) {
register_data_types(store);
#if !PROD_BUILD
setup_reflection(store);
#endif
}
- (映射到上面的#2)
/***** .h *****/
struct DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
};
void register_data(const DataRegistrar& module_registrar, lib::store& store) {
module_registrar.register_data_types(store);
#if !PROD_BUILD
module_registrar.setup_reflection(store);
#endif
}
- (映射到上面的#3)
/***** .h *****/
class DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
friend void register_data(const DataRegistrar& module_registrar, lib::store& store);
};
void register_data(const DataRegistrar& module_registrar, lib::store& store) {
module_registrar.register_data_types(store);
#if !PROD_BUILD
module_registrar.setup_reflection(store);
#endif
}
[1] 应用程序 areas/directories/domains 中的“模块”,而不是 c++20 模块
让我们从#3 开始:
friend
破坏了封装,这就是 Scott Meyers 不建议这样做的原因。我也不建议,除非你真的需要它。 eg: CRTP的private construct + friend trick 防止错字
我建议#1,因为所有工作都是由 class 扩展接口 DataRegistrar
完成的。此外,您不希望其他人 fiddle 与 op_1()
和 op_2()
.
如果使用自由函数,需要暴露op_1()
和op_2()
的实例,增加被滥用的机会。
我想编写一个函数 f
,它以特定顺序调用派生 class 的两个重写虚拟方法 op_1
和 op_2
,无需公开那些超出 f
的方法和包含 classes.
我能想到一些有效或几乎有效的方法,但我也是 c++ 的新手,希望得到一些帮助,以在约定性、可维护性等方面选择“最佳”方法:
- 使
f
成为基础 class (Base
) 的 public 非虚拟方法,它按顺序调用虚拟操作,并将操作设为私有。
/***** .h *****/
class Base {
virtual void op_1() const = 0;
virtual void op_2() const = 0;
public:
void f();
};
/***** .cpp *****/
void Base::f() {
op_1();
op_2();
}
- 制作 ops public 并公开一个非成员非好友助手以按顺序调用它们。
/***** .h *****/
struct Base {
virtual void op_1() const = 0;
virtual void op_2() const = 0;
};
void f(const Base& b,) {
b.op_1();
b.op_2();
}
- 将 ops 保留为私有并公开一个 friend 函数以按顺序调用它们。
/***** .h *****/
class Base {
virtual void op_1() const = 0;
virtual void op_2() const = 0;
friend void f(const Base& b);
};
void f() {
b.op_1();
b.op_2();
}
#1 看起来很糟糕,因为派生的 classes 也可以访问 f
,但它对他们没有用处或用处,可能会被滥用。
#2 解决了这个问题,但封装较少,因为操作是 public.
#3 似乎解决了这两个问题,但我是 C++ 的新手并且对交友事物持冷漠态度——它似乎混淆了接口,我读过的指南(例如 Effective C++ #23)不鼓励在没有明确的情况下使用它需要。这是一个合适的案例吗?
#4 ???可能有更好的方法我看不到。开导我!
特定于应用程序的上下文,以防您需要“为什么”
我需要在应用程序初始化时向第三方数据存储注册一些数据类型 - 我需要按顺序执行 2 个同步操作:
store.data_type<MyAppDataType>()
将应用特定数据类型的 table 添加到商店。store.data_type<MyAppDataType>().member<MemberDataType>("member_name")
生成在步骤 1 中注册的特定于应用程序的数据类型,并设置其字段之一以与库的反射 API 一起使用 - 对于在非生产版本中进行调试非常有用。
操作 #2 是可选的,但如果执行它应该在操作 #1 之后发生。
我希望我的应用程序的各个模块[1] 将它们自己的数据注册到商店,以避免出现一个大而混乱的文件,其中应用程序的所有数据都注册在一起。为此,我在每个模块中包含一个 class,它扩展了一个 DataRegistrar
基础 class - 派生的基础力量 classes 为两者提供特定于模块的实现以上操作:
struct DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
};
在应用程序的其他地方,我可以有一些在应用程序启动时运行的代码,这些代码按顺序为每个模块调用 register_data_types
和 setup_reflection
。以下是在简化问题中映射到上述方法的方法:
- (映射到上面的#1)
/***** .h *****/
class DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
public:
void register_data(lib::store& store);
};
/***** .cpp *****/
void DataRegistrar::register_data(lib::store& store) {
register_data_types(store);
#if !PROD_BUILD
setup_reflection(store);
#endif
}
- (映射到上面的#2)
/***** .h *****/
struct DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
};
void register_data(const DataRegistrar& module_registrar, lib::store& store) {
module_registrar.register_data_types(store);
#if !PROD_BUILD
module_registrar.setup_reflection(store);
#endif
}
- (映射到上面的#3)
/***** .h *****/
class DataRegistrar {
virtual void register_data_types(lib::store& store) const = 0;
virtual void setup_reflection(lib::store& store) const = 0;
friend void register_data(const DataRegistrar& module_registrar, lib::store& store);
};
void register_data(const DataRegistrar& module_registrar, lib::store& store) {
module_registrar.register_data_types(store);
#if !PROD_BUILD
module_registrar.setup_reflection(store);
#endif
}
[1] 应用程序 areas/directories/domains 中的“模块”,而不是 c++20 模块
让我们从#3 开始:
friend
破坏了封装,这就是 Scott Meyers 不建议这样做的原因。我也不建议,除非你真的需要它。 eg: CRTP的private construct + friend trick 防止错字
我建议#1,因为所有工作都是由 class 扩展接口 DataRegistrar
完成的。此外,您不希望其他人 fiddle 与 op_1()
和 op_2()
.
如果使用自由函数,需要暴露op_1()
和op_2()
的实例,增加被滥用的机会。