【问题标题】:Cast member of virtual base class虚拟基类的演员
【发布时间】:2013-04-12 13:15:02
【问题描述】:

我知道这段代码行不通,我也知道原因,但是有替代方法吗?

class A
{
public:
    A(void){}
    virtual ~A(void){}
protected:
    A* parent;
    int a;
};

class B : public virtual A
{
public:
    B(void){}
    virtual ~B(void){}
protected:
    void f(){ ((B*)parent)->a; }
};

不可能将parent 转换为B*,因为A 是一个虚拟基类。不投射 parent 也会产生错误。我希望我不必将所有成员都公开。有人知道如何访问 A::a 吗?

编辑

使用好友不起作用,因为派生自 B 的类无权访问 A::a

【问题讨论】:

  • 如果没有强制转换,它可能会失败,因为您试图访问 B 中 A 的受保护字段。您只需添加一个公共 getter 并将强制转换删除到 B*。

标签: c++ inheritance


【解决方案1】:

一些选项:

  1. a公开
  2. a创建一个公共的setter/getter方法——
  3. 让 B 成为 A 类的朋友(或者只是函数 f()

如果您只想允许 A(或特定函数)访问 A 的成员,则第 3 个选项比其他 2 个更好。另一方面,使用其他 2 个选项,您只能将该成员设为公开(但将对所有人公开)

【讨论】:

  • 1+ 在本例中使用友元类有效,但如果从 B 派生类,则必须将每个类(我正在创建的程序有很多)添加为友元。
  • @JMRC 你可以创建另一个类C,它继承自A并且是A的朋友,并且有一个用于A::a()的公共getter/setter。然后从要访问这些成员的 C 继承。所以 B 类将变为 class B : public virtual A, public C 这样只有 C 需要与 A 成为朋友,并且您可以控制哪些类可以访问 setter/getter(但这可能变得混乱)
【解决方案2】:

从我上面的评论中得到答案,因为我测试过它并且编译得很好。

如果没有强制转换,它可能会失败,因为您试图访问 B 中 A 的受保护字段。您只需添加一个公共 getter 并将强制转换删除到 B*

class A
{
public:
    A(void){}
    virtual ~A(void){}
    int getA() { return a; }
protected:
    A* parent;
    int a;
};

class B : public virtual A
{
public:
    B(void){}
    virtual ~B(void){}
protected:
    void f(){ (parent)->getA(); }
};

【讨论】:

  • 1+,我计划将此作为最后一个资源。虽然它有效,但它让用户可以访问他们不应该更改的变量
  • @JMRC:您可以转发声明 B 类并使其成为 A 类的 friend。它不会将变量暴露给其他类,但 B 可以访问 A 的私有成员。
【解决方案3】:

这行得通:

class A {
public:
    A(void){}
    virtual ~A(void){}
protected:
    A* parent;
    int a;
    int parent_a(){ return parent->a;}
};

class B : public virtual A
{
public:
    B(void){}
    virtual ~B(void){}
protected:
    void f(){ A::parent_a(); }
};

注意:

  • a 不暴露于外界,
  • 检索到的a 必须是正确的,因为B 实际上继承自A,因此在获得a 字段之前成功将parent 动态转换为B 应该返回相同的如上面提供的解决方案。

现在,为什么这行得通?

因为一个类隐含地是它自己的朋友。

【讨论】:

  • 1+ 是的,它确实有效。我试图实现它,但由于属性(如A::a)和指针(如A::parent)的数量,函数的数量变得巨大。当没有更紧凑的内容出现时,我会记住这一点。谢谢。
【解决方案4】:

这就是dynamic_cast 的用途。如果您不想重新设计代码,只需将 C 样式转换为 dynamic_cast

void f() { dynamic_cast<B*>(parent)->a; }

要使其正常工作,A 必须至少具有一个虚函数(就像这个一样)。此外,如果parent 实际上没有指向B 类型的对象,则强制转换将产生一个空指针。

【讨论】:

  • B 不是 A 的完整类型,因此 dynamic_cast 将始终返回 NULL。
  • 对不起,我不明白你的意思。如果您告诉编译器parent 实际上指向B 类型的对象,那么B 最好从A 派生。如果您因为尚未定义 B 而陷入混乱,那是一个完全不同的问题,您必须通过重新组织代码来解决。
  • 我的意思是从BA 的dynamic_cast 将起作用,但是从AB 的dynamic_cast 将返回NULL。向上转换会起作用,向下转换不会。
  • @JMRC - B 派生自A,因此您可以使用dynamic_castA* 转换为B*。即使A 是一个虚拟基地。尝试一下。 (如果你在 inside B 这样做可能会遇到问题,但这就是我提到的代码组织问题)
  • @JMRC - 它返回一个空指针,因为a 指向的对象不是 B。试试dynamic_cast&lt;B*&gt;(ap)
【解决方案5】:
class B : public virtual A
{
public:
    B(void){}
    virtual ~B(void){}
protected:
    void f(){ this->a; }
};

您可以从父类(B 的 A 母类)访问受保护的成员。

【讨论】:

  • 但是他想访问'父'变量的'a',而不是基类。
  • OP 想要从不同的对象访问a
  • 如果B没有int a成员,this-&gt;a从B的方法返回父成员a,所以A::a。如果他想从另一个对象(不是 B 类型或 A 类型)访问a 成员,他必须在 A 类中实现公共 getter 方法。或者我不明白他的问题
  • 在他的例子中,他想从实例parent 访问int a。你有一个 B 类的实例(它是 A 类的继承实例),它通过 parent 属性指向 A 类的另一个实例。
【解决方案6】:

我认为这里的问题不在于如何访问元素a,而是为什么在设计中需要一个菱形层次结构树。您应该首先退后一步,评估您是否真的需要一颗钻石树。

如果您这样做,那么代替您当前的方法,为A 提供一个可以从B 调用的合适的(公共)抽象接口。当您开始直接访问受保护的父属性时,您将组件紧密耦合在一起,从而很容易破坏您的代码,并且很难在应用程序生命周期的后期更改您的设计。

【讨论】:

  • 我有一个UI 类,其中有一个containercontent 类。像 HTML div 这样的类可以在另一个 div 中,并且可以包含 div。使用虚拟基类是必要的。
【解决方案7】:

谢谢大家,我们提到了一些可用的可能性,比如使用朋友和为每个变量创建附加函数。这可能在某些情况下有效,但不适用于我的情况。将每个派生类添加为朋友存在的问题是,当他们想要添加一个类时,它会使库的用户友好性降低。为每个变量添加函数会为某些类添加上百个额外函数。

作为我的最终解决方案,我所做的是创建副本而不是演员表。虽然它会运行一点点慢,代码将保持干净。解决速度问题。访问局部变量比访问全局变量快很多。

这就是现在的样子:

class A
{
public:
    A* parent;
    virtual ~A(void){}
    virtual A & operator = (const A & x)
    {
        a = x.a;
        parent = x.parent;
        return *this;
    }
protected:
    int a;
};

class B : public virtual A
{
public:
    B(void){}
    virtual ~B(void){}
    using A::operator =;
    virtual B & operator = (const B & x)
    {
        A::operator = (x);
        return *this;
    }
    void f(void)
    {
        B p;
        p = *parent;
        int x = p.a;//Allowed.
    }
};

如果您有更好的解决方案,请随时发布。

【讨论】:

    猜你喜欢
    • 2010-11-14
    • 2022-12-13
    • 2011-09-30
    • 2023-03-23
    • 2015-12-15
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2016-05-20
    相关资源
    最近更新 更多