wye*_*r33 5 c++ inheritance c++11
如果我们有钻石继承并使用公共虚拟基类,我们可以阻止第一个构造函数被多次调用.现在,我想对构造函数之外的函数做同样的事情.例如,代码:
#include <iostream>
struct A {
virtual void foo() {
std::cout << "A" << std::endl;
}
};
struct B : virtual public A {
virtual void foo() {
A::foo();
std::cout << "B" << std::endl;
}
};
struct C : virtual public A {
virtual void foo() {
A::foo();
std::cout << "C" << std::endl;
}
};
struct D : public B, public C{
virtual void foo() {
B::foo();
C::foo();
std::cout << "D" << std::endl;
}
};
int main() {
D d;
d.foo();
}
Run Code Online (Sandbox Code Playgroud)
产生结果
A
B
A
C
D
Run Code Online (Sandbox Code Playgroud)
我想修改它以便它只产生
A
B
C
D
Run Code Online (Sandbox Code Playgroud)
什么样的策略或模式可以实现这一目标?
我更喜欢Tony D的答案,而不是以下.尽管如此,理论上可以使用另一个类的构造函数来定义适当的函数层次结构.特别
#include <iostream>
struct A;
struct B;
struct C;
struct D;
namespace foo {
struct A {
A(::A* self);
};
struct B : virtual public A {
B(::B* self);
};
struct C : virtual public A {
C(::C* self);
};
struct D : public B, public C{
D(::D* self);
};
}
struct A {
private:
friend class foo::A;
friend class foo::B;
friend class foo::C;
friend class foo::D;
int data;
public:
A() : data(0) {}
virtual void foo() {
(foo::A(this));
}
void printme() {
std::cout << data << std::endl;
}
};
struct B : virtual public A {
virtual void foo() {
(foo::B(this));
}
};
struct C : virtual public A {
virtual void foo() {
(foo::C(this));
}
};
struct D : public B, public C{
virtual void foo() {
(foo::D(this));
}
};
foo::A::A(::A* self) {
self->data+=1;
std::cout << "A" << std::endl;
}
foo::B::B(::B* self) : A(self) {
self->data+=2;
std::cout << "B" << std::endl;
}
foo::C::C(::C* self) : A(self) {
self->data+=4;
std::cout << "C" << std::endl;
}
foo::D::D(::D* self) : A(self), B(self), C(self) {
self->data+=8;
std::cout << "D" << std::endl;
}
int main() {
D d;
d.foo();
d.printme();
}
Run Code Online (Sandbox Code Playgroud)
基本上,命名空间foo中的类对名为foo的函数进行计算.这似乎有点冗长,所以也许有更好的方法来做到这一点.
再次感谢Tony D澄清上面的例子.是的,基本上上面的做法是创建符合虚拟基本名称的临时变量.通过这种方式,我们可以使用构造函数来防止冗余计算.额外的尝试是试图展示如何访问可能已被埋在基类中的私人成员.稍微考虑一下,还有另一种方法可以做到这一点,取决于应用程序可能会更清洁也可能不会更清洁.我会留在这里供参考.与上一个例子一样,缺点是我们基本上需要手动再次连接继承.
#include <iostream>
struct A {
protected:
int data;
public:
A() : data(0) {}
struct foo{
foo(A & self) {
self.data+=1;
std::cout << "A" << std::endl;
}
};
void printme() {
std::cout << data << std::endl;
}
};
struct B : virtual public A {
struct foo : virtual public A::foo {
foo(B & self) : A::foo(self) {
self.data+=2;
std::cout << "B" << std::endl;
}
};
};
struct C : virtual public A {
struct foo : virtual public A::foo {
foo(C & self) : A::foo(self) {
self.data+=4;
std::cout << "C" << std::endl;
}
};
};
struct D : public B, public C{
struct foo : public B::foo, public C::foo {
foo(D & self) : A::foo(self) , B::foo(self), C::foo(self) {
self.data+=8;
std::cout << "D" << std::endl;
}
};
};
int main() {
D d;
(D::foo(d));
d.printme();
}
Run Code Online (Sandbox Code Playgroud)
本质上,调用(D :: foo(d))创建一个临时的谁的构造函数执行我们想要的操作.我们手动传入对象d以访问内存.由于类foo位于类A..D中,因此可以访问受保护的成员.
只是 polkadotcadaver 想法的实现。这里,Limiter被设计成一种可重用的机制,并且虚拟基类应该有该类型的成员。受控基类函数用于bool Limiter::do_next()询问是否应该“照常”运行或立即返回,而调用基类函数的派生类从限制器获取范围保护对象,如果尚未声明,该对象将获取所有权,并释放它对销毁拥有的任何所有权。
#include <iostream>
class Limiter
{
public:
Limiter() : state_(Unlimited) { }
class Scope
{
public:
Scope(Limiter& l)
: p_(l.state_ == Unlimited ? &l : NULL)
{ if (p_) p_->state_ = Do_Next; }
~Scope() { if (p_) p_->state_ = Unlimited; }
private:
Limiter* p_;
};
Scope get() { return Scope(*this); }
bool do_next()
{
if (state_ == Do_Next) { state_ = Suspended; return true; }
return state_ != Suspended;
}
private:
enum State { Unlimited, Do_Next, Suspended } state_;
};
struct A {
Limiter limiter_;
virtual void foo() {
if (limiter_.do_next())
std::cout << "A" << std::endl;
}
};
struct B : virtual public A {
virtual void foo() {
Limiter::Scope ls = A::limiter_.get();
A::foo();
std::cout << "B" << std::endl;
}
};
struct C : virtual public A {
virtual void foo() {
Limiter::Scope ls = A::limiter_.get();
A::foo();
std::cout << "C" << std::endl;
}
};
struct D : public B, public C{
virtual void foo() {
Limiter::Scope ls = A::limiter_.get();
B::foo();
C::foo();
std::cout << "D" << std::endl;
}
};
int main() {
D d;
d.foo();
}
Run Code Online (Sandbox Code Playgroud)
我花了一段时间才弄清楚你在代码中做了什么;-P - 所以为了讨论,我将发布我将其归结为:
#include <iostream>
namespace foo {
struct A {
A() { std::cout << "A\n"; }
};
struct B : virtual public A {
B() { std::cout << "B\n"; }
};
struct C : virtual public A {
C() { std::cout << "C\n"; }
};
struct D : public B, public C{
D() { std::cout << "D\n"; }
};
}
struct A { virtual void foo() { foo::A(); } };
struct B : virtual public A { void foo() { foo::B(); } };
struct C : virtual public A { void foo() { foo::C(); } };
struct D : public B, public C { void foo() { foo::D(); } };
int main() {
D d;
d.foo();
}
Run Code Online (Sandbox Code Playgroud)
为了其他人的缘故 - 这是通过让A..D::foo()函数创建类型的临时对象来实现的foo::A..D,其构造函数遵循virtual基本指定,因此foo::A::A()仅被调用一次。
作为通用解决方案,一个问题是您必须手动同步结构foo::,因此存在冗余和脆弱性。不过还算聪明吧!