【问题标题】:Accessing a protected member of a superclass-typed member object - an elegant solution访问超类类型成员对象的受保护成员 - 一个优雅的解决方案
【发布时间】:2012-04-14 07:48:16
【问题描述】:

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

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

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

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

  • 更明显的是,我不希望用户直接使用这些信息(我有一些函数可以返回与我的protected 变量相关和/或写入的信息,但是不是变量值本身)
  • 在我的例子中,protected 变量比int 复制要重得多(它是doubles 的二维std::vector),并且将其复制到派生类的实例中是不必要的耗时耗内存

现在,我有两种不同的方法可以让我的代码做我想做的事,但我都不喜欢这两种方法,我正在寻找一个实际上打算做的 C++ 概念类似的东西(我不可能是第一个渴望这种行为的人)。

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

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

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。但是,如果其他人继续我的项目的工作,他可能对代码不太熟悉。此外,将变量指针转换为我非常清楚不是的东西对我来说只是纯粹的邪恶。

我正在努力使这个项目的代码尽可能保持美观和简洁,所以如果有人对不需要不时修改基类或使用邪恶概念的解决方案有任何建议,我非常感谢。

【问题讨论】:

  • 我相信在装饰器模式中,装饰器和实际对象实现一个接口,即装饰器不扩展实际实现的对象,只提供相同的接口,装饰只应用于公共接口。
  • @DavidRodríguez-dribeas 认为我在您输入评论时编辑了解释 :) 您说的是真的,但在我的实际情况下,最上面的界面包含一些常见的数据 每个类的具体实例。因此,从理论上讲,我不想访问属于任何具体实现的任何对象,只是访问在接口中实现的类的每个实例共有的一些对象。但是正如我刚刚在Q中编辑的那样,示例代码并不专注于实现示例装饰器,只是我的问题情况的一个示例。
  • 我很难理解你想要做什么。我知道你想举一个简单的例子,但现在我不知道代码中哪些奇怪的东西很重要,哪些不重要。例如,在 B 的构造函数中,您复制 var 成员,但在 C 类中,您直接使用 myA->var。为什么需要副本,而您不能使用副本进行计算?让我们从那里开始,也许它会变得更清晰。
  • @lucas1024 问题是,原始示例仅显示了我想要访问的内容、位置和方式,但与装饰器模式不太相似(它只包含与真正的装饰器实现相同的对象) .实际上,我在基类中有超过 1 或 2 个 var,简单的(例如标志)我不介意复制,但重的(例如向量 - 只是添加)我 don' t 想要复制。当您留下评论时,我在示例中只使用了 1 个变量,但是我确实在我的代码中添加了我真正想访问的内容的解释。

标签: c++ design-patterns inheritance


【解决方案1】:

我相信您可能想要重新考虑设计,但是对于我如何访问会员?这个特定问题的解决方案可能是:

class A{
protected:
    int var;

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

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

varAccessor( this->myA ) = 5;

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

【讨论】:

  • 您所说的实际上是这里所有 cmets 中最有意义的。我会考虑你在这里写的更多(关于 A 到这个 Q 和设计问题),也许会发布关于我应该如何单独设计系统的问题。我仍然认为装饰器是要走的路,但也许有人会有更好的建议。但在这里,这是题外话。
  • 我想我终于找到了一个很好的方法来做我想做的事......如果这对你来说不是太麻烦,我会很高兴你能评论我的解决方案,因为你的 cmets 和回答到目前为止,让我想到了很多关于我的代码的好问题
【解决方案2】:

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

事实上,我没有在您的示例中看到装饰发生。您刚刚更改了子类中的行为,这向我表明您只想要简单的继承(考虑一下,如果您使用 B 来装饰另一个 B 效果不要结束像在普通装饰中一样链接)。

【讨论】:

  • 是的,我没有在我的代码示例中添加任何实际的装饰,我只编写了最小的示例,它会以我想要访问它们的相同方式访问成员,以保持尽可能关注我的问题。如果您认为这样令人困惑,我现在将扩展我的示例代码。
【解决方案3】:

我想我找到了一种不错的方法来在我拥有的继承结构中做我想做的事。

首先,在 base 类(作为所有其他类的基础,以及装饰器模式中的 抽象基类接口)中,我为第一个子类添加friend class 声明only(作为抽象装饰器接口的子类):

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&lt;&gt;)通过访问函数不需要来制作friends的class A的B的所有子类:

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 中的某些函数提供好友访问权限(将它们声明为 friend 函数)但这不起作用,因为我需要在 @ 中声明函数987654332@ 在class A 中的朋友声明之前。因为在这种情况下class B 继承了class A,这会给我循环依赖(class B 的前向声明不足以仅使用友元函数,但它适用于友元类声明)。

【讨论】:

  • 您让我评论:设计问题仍然存在。这两个接口仍然很混乱,您从不是接口的类型继承只是为了借用接口,然后强制访问不属于您的成员。这些是设计而不是实现的问题。您在这里所拥有的是该设计的实现,它具有与以前相同的问题。
  • @DavidRodríguez-dribeas 非常感谢您的评论。我将设计问题发布为separate 问题,因此希望有人能找到比我目前更好的方法。
猜你喜欢
  • 2012-12-01
  • 2012-05-09
  • 2017-08-01
  • 2016-10-01
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 2012-07-22
相关资源
最近更新 更多