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

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

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

#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 Pattern(B类有我想要装饰的myA内部变量),但是我还想使用A类的一些受保护成员来做“装饰“计算(在B类及其所有子类中).因此,这个例子不是装饰器的正确例子(甚至不是简单的装饰器).在这个例子中,我只专注于演示有问题的功能(我想要使用但我不能).在这个例子中,甚至没有实现Decorator模式所需的所有类/接口(我没有抽象的基类接口,由具体的基类实例继承,以及抽象装饰器接口,用作超类)用于混凝土装饰器).我只提到装饰器的上下文(我想要一个A *指针的原因).

在这种特殊情况下,我认为制作(相当于)int var public(甚至编写一个可公开访问的getter)有两个原因:

>更明显的一个,我不希望用户直接实际使用信息(我有一些函数返回与我的受保护变量相关和/或写入的信息,但不是变量值本身)
>在我的情况下,受保护的变量比int更复杂(它是2D std :: vector of double),并且将其复制到派生类的实例中会不必要地浪费时间和内存

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

到目前为止我有什么,为什么我不喜欢它:

1.将所有(相关的)继承类的朋友声明为基类:

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

变量名对我使用这种方法的感觉很有启发性,但这是我现在正在使用的那个.因为我设计了这个类,所以我知道如何小心并且只使用A类中实际实现的东西,同时将它作为B类处理.但是,如果其他人继续我的项目工作,他可能不是这样熟悉代码.另外,将一个变量指针投射到我非常清楚的事情中,这对我来说不仅仅是纯粹的邪恶.

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

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

class A{
protected:
    int var;

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

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

varAccessor( this->myA ) = 5;

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

点赞