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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3248255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:26:05  来源:igfitidea点击:

Why doesn't C++ have virtual variables?

c++

提问by Bruce

This might have been asked a million times before or might be incredibly stupid but why is it not implemented?

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

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

This code will access A::a. How do I access B::a?

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

采纳答案by Josh Kelley

To access B::a:

要访问 B::a:

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

To explicitly access both A::a and B::a:

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

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

(dynamic_cast is sometimes better than static_cast, but it can't be used here because A and B are not polymorphic.)

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

As to why C++ doesn't have virtual variables: Virtual functions permit polymorphism; in other words, they let a classes of two different types be treated the same by calling code, with any differences in the internal behavior of those two classes being encapsulated within the virtual functions.

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

Virtual member variables wouldn't really make sense; there's no behavior to encapsulate with simply accessing a variable.

虚拟成员变量实际上没有意义;没有任何行为可以通过简单地访问变量来封装。

Also keep in mind that C++ is statically typed. Virtual functions let you change behavior at runtime; your example code is trying to change not only behavior but data typesat runtime (A::ais int, B::ais float), and C++ doesn't work that way. If you need to accommodate different data types at runtime, you need to encapsulate those differences within virtual functions that hide the differences in data types. For example (demo code only; for real code, you'd overload operator<< instead):

还要记住,C++ 是静态类型的。虚函数让你在运行时改变行为;您的示例代码不仅尝试更改行为,还尝试更改运行时的数据类型A::ais intB::ais float),而 C++ 不会那样工作。如果需要在运行时容纳不同的数据类型,则需要将这些差异封装在隐藏数据类型差异的虚函数中。例如(仅演示代码;对于真正的代码,您应该重载 operator<<):

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

Also keep in mind that making member variables public like this can break encapsulation and is generally frowned upon.

还要记住,像这样公开成员变量可能会破坏封装,通常是不受欢迎的。

回答by James Curran

By not making data public, and accessing them through virtual functions.

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

Consider for a moment, how what you ask for would have to be implemented. Basically, it would force any access to any data member to go through a virtual function. Remember, you are accessing data through a pointer to an A object, and class A doesn't know what you've done in class B.

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

In other words, we could make accessing any data member anywhere much slower -- or you could write a virtual method. Guess which C++'s designers chose..

换句话说,我们可以使访问任何地方的任何数据成员的速度慢得多——或者您可以编写一个虚拟方法。猜猜 C++ 的设计师选择了哪些...

回答by Johannes Schaub - litb

You can't do this and C++ does not support it because it breaks with fundamental C++ principles.

你不能这样做,C++ 不支持它,因为它违反了基本的 C++ 原则。

A floatis a different type than an int, and name lookup as well as determining what conversions will be needed for a value assignment happens at compile time. However what is really named by a->aincluding its actual type would only be known at runtime.

Afloatint, 和名称查找以及确定在编译时发生的值分配所需的转换是不同的类型。然而,通过a->a包含其实际类型来真正命名的内容只有在运行时才能知道。

You can use templates to parameterize class A

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

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

Then you can pass the type, however only at compile time for the above mentioned principle's reason.

然后您可以传递类型,但是仅在编译时出于上述原则的原因。

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

回答by Dark

(dynamic_cast<B*>(a))->a? Why do you need that after all? Are virtual functions not enought?

(dynamic_cast<B*>(a))->a? 你为什么需要那个?虚函数还不够吗?

回答by Cogwheel

Leaving aside the argument that virtual methods should be private, virtual methods are intended as an extra layer of encapsulation (encapsulating variations in behavior). Directly accessing fields goes against encapsulation to begin with so it would be a bit hypocritical to make virtual fields. And since fields don't define behavior they merely store data, there isn't really any behavior to be virtualized. The very fact that you have a public int or float is an anti-pattern.

撇开虚方法应该是私有的论点不谈,虚方法旨在作为额外的封装层(封装行为的变化)。直接访问字段一开始就违背了封装,所以制作虚拟字段有点虚伪。而且由于字段不定义行为,它们仅存储数据,因此实际上没有任何要虚拟化的行为。您拥有公共 int 或 float 的事实本身就是一种反模式。

回答by Pablo Santa Cruz

You can downcast your variable to access B::a.

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

Something like:

就像是:

((B*)a)->a

I think it is the same in most OO programming languages. I can't think of any one implementing virtual variablesconcept...

我认为在大多数面向对象编程语言中都是一样的。我想不出任何一个实现virtual variables概念......

回答by Tim

You can create such effect like this:

您可以像这样创建这样的效果:

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

In the example above you could say that the value of A has the same effect as a virtual variable should have.

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

Edit:This is the actual answer to your question, but seeing your code example I noticed that you're seeking for different types in the virtual variable. You could replace double valuewith an union like this:

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

union {
    int intValue;
    float floatValue;
} value

and acces it like:

并像这样访问它:

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

Note, for speed reasons I would avoid this.

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

回答by stands2reason

Because according to the C standard, the offset of a field within a class or struct is required to be a compile-time constant. This also applies to when accessing base class fields.

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

Your example wouldn't work with virtual getters either, as the override requires the same type signature. If that was necessary, your virtual getter would have to return at algebraic type and the receiving code would have to check at run-time if it was of the expected type.

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

回答by Mark B

This isn't supported by C++ because it violates the principles of encapsulation.

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

Your classes should expose and implement a public (possibly virtual) interface that tells class users nothing about the internal workings of your class. The interface should describe operations (and results) that the class can do at an abstract level, not as "set this variable to X".

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