为什么C ++没有虚拟变量?

时间:2010-07-14 16:18:50

标签: c++

这可能已被问过一百万次,或者可能是非常愚蠢但为什么没有实现呢?

class A
{
      public:
             A(){ a = 5;}
             int a;
};

class B:public A
{
      public:
             B(){ a = 0.5;}
              float a;
};

int main()
{
    A * a = new B();

    cout<<a->a;
    getch();
    return 0;
}

此代码将访问A :: a。我如何访问B :: a?

9 个答案:

答案 0 :(得分:35)

访问B :: a:

cout << static_cast<B*>(a)->a;

显式访问A :: a和B :: a:

cout << static_cast<B*>(a)->A::a;
cout << static_cast<B*>(a)->B::a;

(dynamic_cast有时比static_cast更好,但它不能在这里使用,因为A和B不是多态的。)

至于为什么C ++没有虚拟变量:虚函数允许多态;换句话说,他们通过调用代码让两个不同类型的类被视为相同,这两个类的内部行为的任何差异被封装在虚函数中。

虚拟成员变量真的没有意义;只需访问变量就没有封装行为。

还要记住,C ++是静态类型。虚函数允许您在运行时更改行为;您的示例代码尝试不仅在运行时更改行为,而且数据类型A::aintB::afloat)和C ++不这样做。如果需要在运行时容纳不同的数据类型,则需要在虚拟函数中封装这些差异,以隐藏数据类型的差异。例如(仅限演示代码;对于实际代码,您需要overload operator<< instead):

class A
{
  public:
         A(){ a = 5;}
         int a;
         virtual void output_to(ostream& o) const { o << a; }
};

class B:public A
{
  public:
         B(){ a = 0.5;}
         float a;
         void output_to(ostream& o) const { o << a; }
};

另外请记住,像这样公开成员变量可能会破坏封装,并且通常不赞成。

答案 1 :(得分:10)

不公开数据,并通过虚函数访问它们。

请考虑一下,您要求的内容必须如何实施。基本上,它会强制对任何数据成员的任何访问都通过虚函数。请记住,您通过指向A对象的指针访问数据,而A类不知道您在B类中所做的事情。

换句话说,我们可以在任何地方访问任何数据成员 - 或者您可以编写虚拟方法。猜猜哪些C ++的设计师选择了......

答案 2 :(得分:4)

你不能这样做,而且C ++不支持它,因为它打破了基本的C ++原则。

floatint的类型不同,名称查找以及确定在编译时进行值分配所需的转换。然而,a->a真正命名的包括其实际类型的内容只能在运行时知道。

您可以使用模板参数化类A

template<typename T>
class A
{
    public:
        // see also constructor initializer lists
        A(T t){ a = t; }
        T a;
};

然后你可以传递类型,但是只能在编译时传递上述原则的原因。

A<int> a(5); 
A<float> b(5.5f);

答案 3 :(得分:3)

(dynamic_cast<B*>(a))->a? 毕竟你为什么需要这个?虚拟功能还不够吗?

答案 4 :(得分:3)

不考虑virtual methods should be private的论点,虚拟方法旨在作为额外的封装层(封装行为的变化)。直接访问字段反对封装开始,因此制作虚拟字段会有点虚伪。由于字段不定义仅存储数据的行为,因此实际上没有任何虚拟化行为。你有一个公共int或float的事实是反模式。

答案 5 :(得分:2)

您可以向下转换变量以访问B::a

类似的东西:

((B*)a)->a

我认为在大多数OO编程语言中都是一样的。我想不出任何实施virtual variables概念的人......

答案 6 :(得分:2)

您可以创建如下效果:

#include <iostream>

class A {
public:
    double value;

    A() {}
    virtual ~A() {}

    virtual void doSomething() {}
};

class B : public A {
public:

    void doSomething() {
        A::value = 3.14;
    }
};

int main() {
    A* a = new B();
    a->doSomething();
    std::cout << a->value << std::endl;
    delete a;
    return 0;
}

在上面的示例中,您可以说A的值与虚拟变量应具有的效果相同。

编辑:这是您问题的实际答案,但看到您的代码示例后,我注意到您正在寻找虚拟变量中的不同类型。你可以用这样的联合替换double value

union {
    int intValue;
    float floatValue;
} value

并像访问它一样:

a->value.intValue = 3;
assert(a->value.floatValue == 3);

请注意,出于速度原因,我会避免这种情况。

答案 7 :(得分:2)

因为根据C标准,类或结构中字段的偏移量必须是编译时常量。这也适用于访问基类字段时。

您的示例也不适用于虚拟getter,因为覆盖需要相同的类型签名。如果这是必要的,你的虚拟getter必须以代数类型返回,并且接收代码必须在运行时检查它是否是预期的类型。

答案 8 :(得分:1)

C ++不支持这种做法,因为它违反了封装原则。

您的类应该公开并实现一个公共(可能是虚拟的)接口,该接口告诉类用户没有关于您的类的内部工作。接口应描述类可以在抽象级别执行的操作(和结果),而不是“将此变量设置为X”。