首先,我知道我做不到,我认为这不是一个重复的问题(这个问题和这个问题处理同一个问题,但他们只想解释为什么它不起作用(。
所以,我对类和继承有一个类似的概念,我会以某种方式优雅地想要做一些被禁止的事情。这是一个非常简单的代码片段,反映了我想做的事情:
#include <iostream>
class A{
protected:
int var;
std::vector <double> heavyVar;
public:
A() {var=1;}
virtual ~A() {}
virtual void func() {
std::cout << "Default behavior" << this->var << std::endl;
}
// somewhere along the way, heavyVar is filled with a lot of stuff
};
class B: public A{
protected:
A* myA;
public:
B(A &a) : A() {
this->myA = &a;
this->var = this->myA->var;
// copy some simple data, e.g. flags
// but don't copy a heavy vector variable
}
virtual ~B() {}
virtual void func() {
this->myA->func();
std::cout << "This class is a decorator interface only" << std::endl;
}
};
class C: public B{
private:
int lotsOfCalc(const std::vector <double> &hv){
// do some calculations with the vector contents
}
public:
C(A &a) : B(a) {
// the actual decorator
}
virtual ~C() {}
virtual void func() {
B::func(); // base functionality
int heavyCalc = lotsOfCalc(this->myA->heavyVar); // illegal
// here, I actually access a heavy object (not int), and thus
// would not like to copy it
std::cout << "Expanded functionality " << heavyCalc << std::endl;
}
};
int main(void){
A a;
B b(a);
C c(a);
a.func();
b.func();
c.func();
return 0;
}
这样做的原因是我实际上是在尝试实现一个装饰器模式(class B
具有我想要装饰的myA
内部变量(,但我也想在进行"装饰"计算时使用一些受保护的class A
成员(在class B
及其所有子类中(。因此,这个例子不是装饰器的正确例子(甚至不是一个简单的例子(。在示例中,我只专注于演示有问题的功能(我想使用但不能使用的功能(。在这个例子中,甚至没有使用实现装饰器模式所需的所有类/接口(我没有一个抽象的基类接口,由具体的基类实例继承,以及一个抽象的装饰器集成,用作具体装饰器的超类(。我只提到上下文的装饰器(我想要一个A*
指针的原因(。
在这种特殊情况下,我认为将(我的等价物(int var
公开(甚至编写一个可公开访问的getter(没有多大意义,原因有两个:
- 更明显的一个,我不希望用户实际直接使用信息(我有一些函数返回与我的
protected
变量相关和/或写入的信息,但不是变量值本身( - 在我的例子中,
protected
变量比复制int
要重得多(它是double
的 2Dstd::vector
(,并且将其复制到派生类的实例中将不必要地消耗时间和内存
有两种不同的方法让我的代码做我想让它做的事情,但我不喜欢这两种方法,我正在寻找一个C++
概念,它实际上是为了做这种事情(我不能成为第一个想要这种行为的人(。
到目前为止我有什么以及为什么我不喜欢它:
1. 向基类声明所有(相关的(继承类friend
S:
class A{
....
friend class B;
friend class C;
};
我不喜欢这个解决方案,因为它会迫使我每次编写新的子类时修改我的基类,而这正是我试图避免的。(我只想在系统的主要模块中使用"A"接口。
2. 将 A*
指针转换为继承类的指针并使用它
void B::func(){
B *uglyHack = static_cast<B*>(myA);
std::cout << uglyHack->var + 1 << std::endl;
}
变量名称非常暗示我使用这种方法的感觉,但这是我现在正在使用的方法。由于我设计了这个类,我知道如何小心并只使用class A
中实际实现的东西,同时将其视为class B
。但是,如果其他人继续我的项目工作,他可能不太熟悉代码。此外,将变量指针投向我非常清楚的东西,它对我来说不仅仅是纯粹的邪恶。
我试图使这个项目的代码尽可能漂亮和干净,所以如果有人对不需要时不时修改基类或使用邪恶概念的解决方案有任何建议,我将不胜感激。
我相信您可能想重新考虑设计,但是如何访问成员的具体问题的解决方案? 可能是:
class A{
protected:
int var;
static int& varAccessor( A& a ) {
return a.var;
}
};
然后在派生类型中调用受保护的访问器,通过引用传递成员对象:
varAccessor( this->myA ) = 5;
现在,如果您正在考虑装饰器模式,我认为这不是要走的路。混淆的根源在于大多数人没有意识到类型有两个独立的接口,面向用户的public
接口和实现提供程序(即派生类型(的virtual
接口,因为在许多情况下,函数既public
又virtual
(即语言允许绑定两个语义不同的接口(。在装饰器模式中,使用基接口提供实现。继承的存在使得派生类型可以通过一些实际工作(装饰(为用户提供操作,然后将工作转发到实际对象。继承关系不存在,您可以通过受保护的元素以任何方式访问实现对象,这本身就是危险的。如果传递的派生类型的对象对该受保护成员具有更严格的不变量(即对于 X 类型的对象,var
必须是奇数(,则您采用的方法将允许修饰器(各种(破坏应该只修饰的该X
类型的不变量。
我找不到任何以这种方式使用的装饰器模式的例子。看起来在C++中,它用于装饰,然后委托回装饰者的公共抽象接口,而不是从中访问非公共成员。
事实上,我在你的例子中没有看到装饰发生。您刚刚更改了子类中的行为,这向我表明您只想要普通继承(考虑一下,如果您使用B
来装饰另一个B
效果最终不会像在正常装饰中那样链接(。
我想我在我拥有的继承结构中找到了一种很好的方法来做我想做的事情。
首先,在基类(所有其他类的基类,以及装饰器模式中的抽象基类接口(中,我只为第一个子类(将充当抽象装饰器接口的子类(添加一个friend class
声明:
class A{
....
friend class B;
};
然后,我在子类中添加protected
访问函数,用于基类中的所有相关变量:
class B : public A{
...
protected:
A *myA;
int getAVar() {return myA->var;}
std::vector <double> &getAHeavyVar {return myA->heavyVar;}
};
最后,我可以通过访问函数以受控方式(而不是static_cast<>
(从继承class B
的所有类(与相反(访问我需要的东西,而无需使 B 的所有子类friend
class A
class C : public B{
....
public:
virtual void func() {
B::func(); // base functionality
int heavyCalc = lotsOfCalc(this->getAHeavyVar); // legal now!
// here, I actually access a heavy object (not int), and thus
// would not like to copy it
std::cout << "Expanded functionality " << heavyCalc << std::endl;
std::cout << "And also the int: " << this->getAVar << std::endl;
// this time, completely legal
}
};
我还试图只为class B
中的某些函数提供朋友访问权限(将它们声明为朋友函数(,但这不起作用,因为我需要在 class B
中的函数在 class A
中声明之前。由于在这种情况下class B
继承class A
,这将给我循环依赖(class B
的前向声明不足以仅使用友元函数,但它适用于友元类声明(。