C++ 仅针对函数的多重继承-无虚拟和CRTP
如何仅为函数执行多重继承 必须共享基类的数据 没有虚拟功能假定vtable是昂贵的 避免虚拟继承 实现必须能够驻留在.cpp中 允许使用c++14 以下是类似的问题: -使用虚拟继承。虚拟继承是和。 -重点是语法和编译,而不是编程技术。 、C++03和-实现必须位于标头中 以下是示例代码:- 它确实管用,但看起来不雅观。 此外,实现必须在头中,因为OA和OB是模板类 有更好的方法吗?还是这条路要走 抱歉,如果这是一个太新的问题或已经被问过了。我是C++初学者。 编辑C++ 仅针对函数的多重继承-无虚拟和CRTP,c++,c++14,multiple-inheritance,crtp,C++,C++14,Multiple Inheritance,Crtp,如何仅为函数执行多重继承 必须共享基类的数据 没有虚拟功能假定vtable是昂贵的 避免虚拟继承 实现必须能够驻留在.cpp中 允许使用c++14 以下是类似的问题: -使用虚拟继承。虚拟继承是和。 -重点是语法和编译,而不是编程技术。 、C++03和-实现必须位于标头中 以下是示例代码:- 它确实管用,但看起来不雅观。 此外,实现必须在头中,因为OA和OB是模板类 有更好的方法吗?还是这条路要走 抱歉,如果这是一个太新的问题或已经被问过了。我是C++初学者。 编辑 请给出使用的扩展示例 在EC
请给出使用的扩展示例 在ECS中,它在某些情况下很有用:-
class O{
protected: EntityHandle e;
};
class ViewAsPhysic : public O{ //A
public: void setTransform(Transformation t){
Ptr<PhysicTransformComponent> g=e;
g->transform=t;
}
};
class ViewAsLight : public O{ //B
public: void setBrightness(int t){
Ptr<LightComponent> g=e;
g->clan=t;
}
};
class ViewAsLightBlock : public O{ //AB
//both functions
};
开始讨论
class O
{
// no virtual destructor. So cant use polymorphic deletion
// like :
// O *o = new AB;
// delete o;
protected: int database=0;
};
class A : virtual public O{
public: void print(){
std::cout<<database<<std::endl;
}
};
class B : virtual public O{
public: void set(int s){
database=s+1;
}
};
class AB : protected A, protected B{}; // no vtable
void foo() {
AB ab;
ab.print(); // won't perform virtual call.
}
开始讨论
class O
{
// no virtual destructor. So cant use polymorphic deletion
// like :
// O *o = new AB;
// delete o;
protected: int database=0;
};
class A : virtual public O{
public: void print(){
std::cout<<database<<std::endl;
}
};
class B : virtual public O{
public: void set(int s){
database=s+1;
}
};
class AB : protected A, protected B{}; // no vtable
void foo() {
AB ab;
ab.print(); // won't perform virtual call.
}
像这样的
必须共享基类的数据-检查
没有虚拟功能假设vtable很昂贵-检查
避免虚拟继承-检查
实现必须能够驻留在.cpp-check中
允许使用c++14-检查。使用c++11。
像这样的
必须共享基类的数据-检查
没有虚拟功能假设vtable很昂贵-检查
避免虚拟继承-检查
实现必须能够驻留在.cpp-check中
允许使用c++14-检查。使用c++11。
这里的问题是数据库字段是类O的成员。因此,如果没有虚拟继承,A和B将各自拥有自己的数据库副本。所以你必须找到一种方法来强迫a和B共享相同的值。例如,您可以使用在受保护构造函数中初始化的引用字段:
#include <iostream>
class O{
int _db;
protected: int &database;
O(): database(_db) {};
O(int &db): database(db) {};
};
class A : public O{
public: void print(){
std::cout<<database<<std::endl;
}
A() {} // public default ctor
protected: A(int& db): O(db) {}; // protectect ctor
};
class B : public O{
public: void set(int s){
database=s+1;
}
B() {} // public default ctor
protected: B(int& db): O(db) {}; // protectect ctor
};
class AB : public A, public B {
int _db2;
public: AB(): A(_db2), B(_db2) {}; // initialize both references to same private var
};
int main() {
AB ab;
ab.set(1);
ab.print();
return 0;
}
上面的代码不使用虚拟继承、虚拟函数和模板,所以方法可以安全地在cpp文件中实现。AB类实际上使用了来自其双亲的方法,并且对其底层数据仍有一致的看法。事实上,它通过在最派生的类中构建公共数据并通过其父类中受保护的构造函数注入来模拟显式虚拟继承。这里的问题是数据库字段是类O的成员。因此,如果没有虚拟继承,A和B将各自拥有自己的数据库副本。所以你必须找到一种方法来强迫a和B共享相同的值。例如,您可以使用在受保护构造函数中初始化的引用字段:
#include <iostream>
class O{
int _db;
protected: int &database;
O(): database(_db) {};
O(int &db): database(db) {};
};
class A : public O{
public: void print(){
std::cout<<database<<std::endl;
}
A() {} // public default ctor
protected: A(int& db): O(db) {}; // protectect ctor
};
class B : public O{
public: void set(int s){
database=s+1;
}
B() {} // public default ctor
protected: B(int& db): O(db) {}; // protectect ctor
};
class AB : public A, public B {
int _db2;
public: AB(): A(_db2), B(_db2) {}; // initialize both references to same private var
};
int main() {
AB ab;
ab.set(1);
ab.print();
return 0;
}
上面的代码不使用虚拟继承、虚拟函数和模板,所以方法可以安全地在cpp文件中实现。AB类实际上使用了来自其双亲的方法,并且对其底层数据仍有一致的看法。事实上,它通过在最派生的类中构建公共数据并通过其父类中受保护的构造函数注入来模拟显式虚拟继承。我们首先定义可以打印的内容和可以设置的内容的概念:
namespace util {
template<class Base, class Derived, class R=void>
using if_base = std::enable_if_t< std::is_base_of< std::decay_t<Base>, std::decay_t<Derived>>::value, R >;
struct stub {};
}
namespace concepts {
template<class Token>
void do_print(Token, util::stub const&)=delete;
template<class Token>
void do_set(Token, util::stub&, int)=delete;
struct has_print {
struct token { friend struct has_print; private: token(int){} };
template<class T>
friend util::if_base<has_print, T> print(T const& t) {
do_print(get_token(), t);
}
private: static token get_token() { return 0; }
};
struct has_set {
struct token { friend struct has_set; private: token(int){} };
template<class T>
friend util::if_base<has_set, T> set(T& t, int x) {
do_set(get_token(),t, x);
}
private: static token get_token() { return 0; }
};
}
其中最困难的部分是访问控制
我确保除非通过has_set::set,否则无法调用do_set
这就是所有这些代币的意义所在。如果您只想说不要调用do_u函数,或者给它们取另一个名字,比如private_impl_set,那么您可以去掉它们和它们的开销
.我们首先定义可以打印的东西和可以设置的东西的概念:
namespace util {
template<class Base, class Derived, class R=void>
using if_base = std::enable_if_t< std::is_base_of< std::decay_t<Base>, std::decay_t<Derived>>::value, R >;
struct stub {};
}
namespace concepts {
template<class Token>
void do_print(Token, util::stub const&)=delete;
template<class Token>
void do_set(Token, util::stub&, int)=delete;
struct has_print {
struct token { friend struct has_print; private: token(int){} };
template<class T>
friend util::if_base<has_print, T> print(T const& t) {
do_print(get_token(), t);
}
private: static token get_token() { return 0; }
};
struct has_set {
struct token { friend struct has_set; private: token(int){} };
template<class T>
friend util::if_base<has_set, T> set(T& t, int x) {
do_set(get_token(),t, x);
}
private: static token get_token() { return 0; }
};
}
其中最困难的部分是访问控制
我确保除非通过has_set::set,否则无法调用do_set
这就是所有这些代币的意义所在。如果您只想说不要调用do_u函数,或者给它们取另一个名字,比如private_impl_set,那么您可以去掉它们和它们的开销
.上面的代码示例要好得多,但效率绝不会降低。如果要避免标记为重复的片段,请将它们写入基类non-virtual(如果需要)prefer@davidhigh它包含重复的代码->导致可维护性问题:若您不需要虚拟调用,编译器将进行设备化优化,所以您可以使用继承而无需花费:AB*AB=new AB;ab->打印@CPP初学者:我刚刚添加了你的观点,但是你更快了:-@user5821508有时是真的,有时不是真的vtable查找需要:请参见。上面的代码示例更好,而且效率也不会更低。如果要避免标记为重复的片段,请将它们写入基类non-virtual(如果需要)prefer@davidhigh它包含重复的代码->导致可维护性问题:若您不需要虚拟调用,编译器将进行设备化优化,所以您可以使用继承而无需花费:AB*AB=new AB;ab->打印@CPP初学者:我刚刚添加了你的观点,但是你更快了:-@user5821508有时是真的,有时不是真的vtable查找需要:参见。对于B*->打印,可以有vtable开销没有?你要去吗
像这样使用吗?如果否,那么答案是否。如果没有声明虚拟函数,则没有vtable是,我将使用所有这些函数A*->,B*->,AB*->。对不起,如果问题不清楚,请扩展使用示例。我添加了一个更现实的示例。我希望它能说服你。对于B*->打印,可以有可变开销吗?你打算这样用吗?如果否,那么答案是否。如果没有声明虚拟函数,则没有vtable是,我将使用所有这些函数A*->,B*->,AB*->。对不起,如果问题不清楚,请扩展使用示例。我添加了一个更现实的示例。我希望它能说服您。我不认为print可以移动到.cpp,因为implements\u print是一个模板类。我错过了什么吗?它可以调用cpp中的另一个函数来完成O上的工作。@cpp初学者请参见第二个使用合成的示例-它将实际打印延迟到另一个编译单元中的自由函数。谢谢,这是有用的,同时也是痛苦的+1在实践中,我应该使用虚拟函数/模板吗?我开始认为在某些方面脏是不可避免的。@cpp初学者我不知道你真正的用例是什么。crtp+继承通常是一个很好的全方位工具。如果你认为虚拟函数很慢,我怀疑你是在一个错误的前提下工作。事实并非如此。它们需要比非虚拟函数多获取一次内存。任何其他多态的替代方法,如开关或携带函数指针,都不会更快。我不认为print可以移动到.cpp,因为implements\u print是一个模板类。我错过了什么吗?它可以调用cpp中的另一个函数来完成O上的工作。@cpp初学者请参见第二个使用合成的示例-它将实际打印延迟到另一个编译单元中的自由函数。谢谢,这是有用的,同时也是痛苦的+1在实践中,我应该使用虚拟函数/模板吗?我开始认为在某些方面脏是不可避免的。@cpp初学者我不知道你真正的用例是什么。crtp+继承通常是一个很好的全方位工具。如果你认为虚拟函数很慢,我怀疑你是在一个错误的前提下工作。事实并非如此。它们需要比非虚拟函数多获取一次内存。任何其他多态的替代品,如开关或携带函数指针,都不会更快。看起来这是一个很棒的答案,我明天会重新阅读/测试它。非常感谢。你真的必须删除复制/移动操作,因为上面的设计毫无意义。看起来这是一个很棒的答案,我明天会重新阅读/测试它。非常感谢。您确实必须删除复制/移动操作,因为上面的设计毫无意义。
2
namespace util {
template<class Base, class Derived, class R=void>
using if_base = std::enable_if_t< std::is_base_of< std::decay_t<Base>, std::decay_t<Derived>>::value, R >;
struct stub {};
}
namespace concepts {
template<class Token>
void do_print(Token, util::stub const&)=delete;
template<class Token>
void do_set(Token, util::stub&, int)=delete;
struct has_print {
struct token { friend struct has_print; private: token(int){} };
template<class T>
friend util::if_base<has_print, T> print(T const& t) {
do_print(get_token(), t);
}
private: static token get_token() { return 0; }
};
struct has_set {
struct token { friend struct has_set; private: token(int){} };
template<class T>
friend util::if_base<has_set, T> set(T& t, int x) {
do_set(get_token(),t, x);
}
private: static token get_token() { return 0; }
};
}
namespace DB {
class O;
void do_print(::concepts::has_print::token, O const& o);
void do_set(::concepts::has_set::token, O& o, int);
class O{
protected: int database=0;
friend void do_print(::concepts::has_print::token, O const&);
friend void do_set(::concepts::has_set::token, O&, int);
};
class A : public O, public concepts::has_print {
};
class B : public O, public concepts::has_set {
};
class AB : public O, public concepts::has_print, concepts::has_set {
};
}
void DB::do_print(::concepts::has_print::token, O const& o ) { std::cout << o.database << std::endl; }
void DB::do_set(::concepts::has_set::token, O& o, int x) { o.database = x+1; }