C++最接近追溯定义已定义类的超类是什么?
What's the closest thing in C++ to retroactively defining a superclass of a defined class?
假设我有这个类
class A {
protected:
int x,y;
double z,w;
public:
void foo();
void bar();
void baz();
};
在我的代码和其他代码中定义和使用。现在,我想写一些可以很好地在 A 上运行的库,但它实际上更通用,并且能够操作:
class B {
protected:
int y;
double z;
public:
void bar();
};
我确实希望我的库是通用的,所以我定义了一个 B 类,这就是它的 API 所采用的。
我希望能够告诉编译器 - 不是在我不再控制的 A 的定义中,而是在其他地方,可能在 B 的定义中:
看,请试着把
B
想象成A
的超阶级。因此,特别是,将其布置在内存中,以便如果我将A*
重新解释为B*
,我的代码期望B*
s将起作用。然后请实际接受A*
作为B*
(A&
作为B&
等)。
C++我们可以用另一种方式做到这一点,即如果 B 是我们无法控制的类,我们可以用class A : public B { ... }
执行"子类 a 已知类"操作;我知道C++没有相反的机制——"超类 A 已知类 A 由新类 B "。我的问题是 - 这种机制最接近可实现的近似值是什么?
笔记:
- 这都是严格的编译时,而不是运行时。
class A
没有任何变化。我只能修改B
的定义和同时知道A
和B
的代码。其他人仍然会使用类A
,如果我希望我的代码与他们的代码交互,我也会这样做。- 这最好是"可扩展"到多个超类的。所以也许我也有
class C { protected: int x; double w; public: void baz(); }
也应该表现得像A
的超类.
您可以执行以下操作:
class C
{
struct Interface
{
virtual void bar() = 0;
virtual ~Interface(){}
};
template <class T>
struct Interfacer : Interface
{
T t;
Interfacer(T t):t(t){}
void bar() { t.bar(); }
};
std::unique_ptr<Interface> interface;
public:
template <class T>
C(const T & t): interface(new Interfacer<T>(t)){}
void bar() { interface->bar(); }
};
这个想法是在幕后使用类型擦除(即Interface
和Interfacer<T>
类)来允许C
接受任何可以调用bar
的内容,然后您的库将获取类型为C
的对象。
我知道C++没有相反的机制 - "超类 A 已知 类">
哦,是的,确实如此:
template <class Superclass>
class Class : public Superclass
{
};
然后你走了。不用说,一切都在编译时。
如果你有一个无法更改的class A
,需要将其插入继承结构中,那么在
template<class Superclass>
class Class : public A, public Superclass
{
};
请注意,dynamic_cast
将在给定指针的情况下到达A*
指针Superclass*
反之亦然。同上Class*
指针。此时,您正在接近组合、特征和概念。
普通模板会这样做,编译器会在您错误使用它们时通知您。
而不是
void BConsumer1(std::vector<B*> bs)
{ std::for_each(bs.begin(), bs.end(), &B::bar); }
void BConsumer2(B& b)
{ b.bar(); }
class BSubclass : public B
{
double xplusz() const { return B::x + B::z; }
}
你写
template<typename Blike>
void BConsumer1(std::vector<Blike*> bs)
{ std::for_each(bs.begin(), bs.end(), &Blike::bar); }
template<typename Blike>
void BConsumer2(Blike& b)
{ b.bar(); }
template<typename Blike>
class BSubclass : public Blike
{
double xplusz() const { return Blike::x + Blike::z; }
}
你使用 BConsumer1 和 BConsumer2 像
std::vector<A*> as = /* some As */
BConsumer1(as); // deduces to BConsumer1<A>
A a;
BConsumer2(a); // deduces to BConsumer2<A>
std::vector<B*> bs = /* some Bs */
BConsumer1(bs); // deduces to BConsumer1<B>
// etc
你会有BSubclass<A>
和BSubclass<B>
,作为使用B
接口做某事的类型。
如果不改变类,就无法更改类的行为。实际上,在已经定义了父类之后,实际上没有添加父类A
机制。
我只能修改B 的定义和同时知道 A 和 B 的代码。
您无法更改A
,但可以更改使用A
的代码。所以你可以,而不是使用A
,简单地使用另一个继承自B
的类(我们称之为D
)。我认为这是理想机制中最接近可实现的。
如果有用,D
可以将A
重用为子对象(可能作为基础)。
这最好是"可扩展"到多个超类的。
D
可以根据需要继承任意数量的超类。
演示:
class D : A, public B, public C {
public:
D(const A&);
void foo(){A::foo();}
void bar(){A::bar();}
void baz(){A::baz();}
};
现在,D
的行为与A
的行为完全一样,如果只有A
继承了B
和C
。
公开继承A
将允许摆脱所有委派样板:
class D : public A, public B, public C {
public:
D(const A&);
};
但是,我认为这可能会在不使用B
的情况下使用A
的代码和使用知道B
(因此使用D
)的代码之间造成混淆。使用D
的代码可以很容易地处理A
,但不能反过来'舍入'。
根本不继承A
而是使用成员将允许您不复制A
来创建D
,而是引用现有的成员:
class D : public B, public C {
A& a;
public:
D(const A&);
void foo(){a.foo();}
void bar(){a.bar();}
void baz(){a.baz();}
};
这显然有可能使对象生存期出错。这可以通过共享指针来解决:
class D : public B, public C {
std::shared_ptr<A> a;
public:
D(const std::shared_ptr<A>&);
void foo(){a->foo();}
void bar(){a->bar();}
void baz(){a->baz();}
};
但是,如果不知道B
或D
的其他代码也使用共享指针,这可能只是一个选项。
这似乎更像是静态多态性,而不是动态的。 正如@ZdeněkJelínek已经提到的,你可以提供一个模板来确保在编译时传递正确的接口。
namespace details_ {
template<class T, class=void>
struct has_bar : std::false_type {};
template<class T>
struct has_bar<T, std::void_t<decltype(std::declval<T>().bar())>> : std::true_type {};
}
template<class T>
constexpr bool has_bar = details_::has_bar<T>::value;
template<class T>
std::enable_if_t<has_bar<T>> use_bar(T *t) { t->bar(); }
template<class T>
std::enable_if_t<!has_bar<T>> use_bar(T *) {
static_assert(false, "Cannot use bar if class does not have a bar member function");
}
这应该做你想做的事(即对任何类使用 bar),而不必求助于 vtable 查找,也无需修改类。 此级别的间接寻址应使用设置适当的优化标志进行内联。 换句话说,您将拥有直接调用 bar 的运行时效率。
- 模板转换为超类
- 为什么我不能在主函数之外定义一个类的对象(它继承了另一个类)?
- 创建包装升压适配器的自定义范围类
- 从用户定义的类生成格式字符串?
- 我的超类中的模板问题与结构定义
- 为用户定义的类正确调用复制构造函数/赋值运算符
- C++ 向量与用户定义的类比较?(==, <, >)
- 使用成员函数和存储值定义书籍类
- C++:用户定义的类,以成员字段作为地址
- C++为API中定义的结构创建超类
- 用户定义的构造函数重载与参数超类的重载不匹配
- 对超类方法的未定义引用
- C++最接近追溯定义已定义类的超类是什么?
- 为超类提供对尚未定义的枚举类型的访问权限
- 模板超类的静态成员定义
- 从超类函数返回子类定义
- 如何迭代定义为超类的向量,但调用子类方法
- c++超类为子类定义静态成员变量
- 通过值将子类对象传递给接受超类对象的函数是定义良好的行为吗
- 当子类在超类中定义为纯虚拟时,如何调用子类中的所有函数