如何只为函数做多重继承?
- 必须共享基类的数据
- 没有虚函数(假设 vtable 很贵(
- 避免虚拟继承
- 实现必须能够驻留在.cpp
- 允许使用 C++14
这里有类似的问题:-
- 仅具有函数的菱形多重继承 - 使用虚拟继承。 虚拟继承通常是不好且昂贵的。
- 没有虚拟继承的多重继承 - 侧重于语法和编译,而不是编程技术。
- C++中的多级继承(CRTP(,CRTP和多级继承,消除CRTP和多重继承的冗余(C++03(和将CRTP与虚拟继承一起使用-实现必须在标头中
这是一个示例代码(coliru demo(:-
class O{
protected: int database=0;
};
class A : public O{
public: void print(){
std::cout<<database<<std::endl;
}
};
class B : public O{
public: void set(int s){
database=s+1;
}
};
class AB : public O{
public: void print(){//duplicate
std::cout<<database<<std::endl;
}
public: void set(int s){//duplicate
database=s+1;
}
};
//AB ab; ab.set(1); ab.print(); // would print 2
这是我的尝试(魔杖盒演示(。 我滥用CRTP:(
:-
class O{
public: int database=0;
};
template<class T>class OA{
public: void print(){
std::cout<<static_cast<T*>(this)->database<<std::endl;
}
};
template<class T>class OB{
public: void set(int s){
static_cast<T*>(this)->database=s+1;
}
};
class A :public O,public OA<A>{};
class B :public O,public OB<B>{};
class AB :public O,public OA<AB>,public OB<AB>{};
它有效,但看起来不优雅。
此外,实现必须在标头中(因为OA
和OB
是模板类(。
有没有更好的方法? 或者这是要走的路?
抱歉,如果这是太新手的问题或已经问过。 我是一个C++初学者。
编辑
请给出使用扩展的示例。
在 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
};
这里的问题是database
字段是类 O 的成员。因此,如果没有虚拟继承,A 和 B 将各自拥有自己的database
副本。所以你必须找到一种方法来强制 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;
}
按预期显示:
2
上面的代码不使用虚拟继承,没有虚拟函数,也没有模板,因此方法可以在cpp文件中安全地实现。AB 类实际上使用来自其父级的方法,并且对其基础数据仍然具有连贯的观点。事实上,它通过在派生最多的类中构建公共数据并通过其父类中的受保护构造函数注入来模拟显式虚拟继承。
像这样的东西?
- 必须共享基类的数据 - 检查
- 没有虚拟函数(假设 vtable 很贵( - 检查
- 避免虚拟继承 - 检查
- 实现必须能够驻留在.cpp检查中 允许使用 C++
- 14 - 检查。使用了 C++11。
#include <iostream>
class O {
protected:
int database = 0;
};
/*
* the concept of implementing print for a base class
*/
template<class...Bases>
struct implements_print : Bases... {
void print() const {
std::cout << this->database << std::endl;
}
};
/*
* The concept of implementing set for a base class
*/
template<class...Bases>
struct implements_set : Bases... {
void set() {
++this->database;
}
};
struct B : implements_set<O> {
};
struct A : implements_print<O> {
};
struct AB : implements_set<implements_print<O>> {
};
int main() {
A a;
a.print();
B b;
b.set();
AB ab;
ab.set();
ab.print();
}
另一种方式,使用组合和访问类来提供对受保护成员的访问。此示例演示如何将database
的工作推迟到另一个编译单元:
#include <iostream>
/*
* this stuff in cpp
*/
namespace implementation
{
void print(const int& database) {
std::cout << database << std::endl;
}
void set(int& database) {
++database;
}
}
/*
* this stuff in header
*/
struct OAccess;
class O {
private:
int database = 0;
friend OAccess;
};
struct OAccess {
template<class Host>
constexpr decltype(auto) database(Host &host) const { return (host.database); } // note: () makes reference
template<class Host>
constexpr decltype(auto) database(Host const &host) const { return (host.database); } // note: () makes reference
};
/*
* the concept of implementing print for a derived class
*/
template<class Host>
struct implements_print {
void print() const {
OAccess access;
implementation::print(access.database(self()));
}
private:
decltype(auto) self() const { return static_cast<Host const &>(*this); }
};
/*
* The concept of implementing set for a derived class
*/
template<class Host>
struct implements_set {
void set() {
OAccess access;
implementation::set(access.database(self()));
}
private:
decltype(auto) self() { return static_cast<Host &>(*this); }
};
template<template<class> class...Impls>
struct OImpl : Impls<OImpl<Impls...>> ..., O {
};
using B = OImpl<implements_set>;
using A = OImpl<implements_print>;
using AB = OImpl<implements_print, implements_set>;
int main() {
A a;
a.print();
B b;
b.set();
AB ab;
ab.set();
ab.print();
}
我们首先定义可以打印的事物和可以设置的事物的概念:
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; }
};
}
然后,我们声明O
以及您可以支持的操作:
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; }
其中最难的部分是访问控制。
我确保除非通过has_set::set
,否则无法拨打do_set
。
这就是所有这些token
的意义所在。 如果你愿意说"不要调用do_
函数"(也许给它们另一个名字,比如private_impl_set
(,你可以去掉它们和它们的开销。
活生生的例子。
开始讨论。
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.
}