访问超类型成员对象的受保护成员 - 一个优雅的解决方案

时间:2012-03-30 15:37:07

标签: c++ design-patterns inheritance

首先,我知道我不能这样做,我认为这不是一个重复的问题(thisthis问题处理相同的问题,但他们只想解释为什么它不起作用。)

所以,我有类似的类和继承的概念,我会以某种方式优雅地想要做一些被禁止的事情。这是一个非常简单的代码片段,反映了我想要做的事情:

#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;
}

这样做的原因是我实际上是在尝试实现Decorator Patternclass B具有我要装饰的myA内部变量),但我也想在进行“装饰”计算时(在class A及其所有子类中)使用class B的一些受保护成员。因此,这个例子不是装饰器的正确例子(甚至不是简单的例子)。在示例中,我只专注于演示有问题的功能(我想要使用但我不能)。在这个例子中,甚至没有使用实现Decorator模式所需的所有类/接口(我没有抽象基类接口,由具体基类实例继承以及抽象装饰器intreface ,用作具体装饰器的超类。我只提到装饰器的上下文(我想要一个A*指针的原因。)

在这种特殊情况下,由于两个原因,我认为(我的等同于)int var公开(甚至写一个公开访问的getter)没有多大意义:

  • 更明显的一个,我希望用户直接实际使用这些信息(我有一些函数可以返回与protected个变量相关和/或写入的信息,但不是变量值本身)
  • 我的案例中的protected变量要比intstd::vector的{​​{1}}更复杂,并将其复制到
  • ,派生类的实例将不必要地耗费时间和内存

现在,我有两种不同的方式让我的代码做我想做的事情,但我不喜欢它们,我正在寻找一个double概念,它实际上是有意的做这种事情(我不能成为第一个希望这种行为的人)。

到目前为止我所拥有的以及为什么我不喜欢它:

1。将所有(相关)继承的类C++声明为基类:

friend

我不喜欢这个解决方案,因为每次编写新的子类类时,它都会强迫我修改我的基类,这正是我的意思我试图避免。 (我只想在系统的主模块中使用'A'接口。)

2。将class A{ .... friend class B; friend class C; }; 指针转换为继承类的指针并使用

A*

变量名称对我使用这种方法的感觉非常有启发性,但这是我现在正在使用的那个。自从我设计了这些类以来,我知道如何小心并且只使用void B::func(){ B *uglyHack = static_cast<B*>(myA); std::cout << uglyHack->var + 1 << std::endl; } 中实际实现的内容,同时将其视为class A。但是,如果其他人继续我的项目工作,他可能不太熟悉代码。另外,将一个变量指针转换为我非常清楚它的东西,对我来说只是感觉很邪恶。

我试图让这些项目的代码尽可能干净利落,所以如果有人对解决方案有任何建议,不需要时不时地修改基类或使用邪恶的概念,我我非常感激。

3 个答案:

答案 0 :(得分:2)

我相信您可能想重新考虑设计,但是如何访问该成员的具体问题的解决方案?可能是:

class A{
protected:
    int var;

    static int& varAccessor( A& a ) {
       return a.var;
    }
};

然后在派生类型中调用受保护的访问器,通过引用传递成员对象:

varAccessor( this->myA ) = 5;

现在,如果您正在考虑装饰器模式,我认为这不是可行的方法。 混淆的根源在于,大多数人都没有意识到类型有两个独立的接口,即用户的public接口和实现提供者的virtual接口(即派生类型),在许多情况下函数是publicvirtual(即语言允许绑定两个语义不同的接口)。在Decorator模式中,您使用基接口来提供实现。继承是存在的,因此派生类型可以通过一些实际工作(装饰)用户提供操作,然后将工作转发到实际对象。继承关系不是通过受保护元素以任何方式访问实现对象的,并且这本身就很危险。如果传递的派生类型的对象具有关于该受保护成员的更严格的不变量(即对于类型为X的对象,var必须是奇数),您正在采用的方法将允许装饰器(of of sort)打破了应该装饰的X类型的不变量。

答案 1 :(得分:0)

我找不到以这种方式使用的装饰器模式的任何示例。它看起来像在C ++中用于装饰,然后委托给decoratee的公共抽象接口,而不是从它访问非公共成员。

事实上,我没有在你的例子中看到装饰的发生。你刚刚改变了子类中的行为,它向我表明你只想要简单的继承(考虑一下,如果你使用B来装饰另一个B,那么效果就不会最终像正常装饰那样链接)。

答案 2 :(得分:0)

我认为我找到了一种很好的方法,可以在我的继承结构中做我想要的。

首先,在 base 类(作为所有其他类的基础的类,以及Decorator模式中的抽象基类接口)中,为第一个子类(将充当抽象装饰器接口的子类)添加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;}
};

最后,我可以从受控方式继承class B(那些将成为具体装饰器)的所有类中访问我需要的东西(而不是static_cast<>)通过访问函数而不需要来生成friend的B 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,这将给我循环依赖({{1>}的前向声明仅仅使用朋友函数是不够的,但是它有效对于朋友类声明很好。)