Why doesn't C++ have virtual variables?

C++

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

To access B::a:

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

To explicitly access both A::a and 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.)

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.

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 types at runtime (A::a is int, B::a is 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):

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.

Solution 2 - C++

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.

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..

Solution 3 - C++

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

A float is 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->a including its actual type would only be known at runtime.

You can use templates to parameterize class 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);

Solution 4 - C++

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

Solution 5 - C++

You can downcast your variable to access 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 variables concept...

Solution 6 - C++

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.

Solution 7 - C++

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.

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 value with an union like this:

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.

Solution 8 - C++

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.

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.

Solution 9 - C++

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

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".

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBruceView Question on Stackoverflow
Solution 1 - C++Josh KelleyView Answer on Stackoverflow
Solution 2 - C++James CurranView Answer on Stackoverflow
Solution 3 - C++Johannes Schaub - litbView Answer on Stackoverflow
Solution 4 - C++DarkView Answer on Stackoverflow
Solution 5 - C++Pablo Santa CruzView Answer on Stackoverflow
Solution 6 - C++CogwheelView Answer on Stackoverflow
Solution 7 - C++TimView Answer on Stackoverflow
Solution 8 - C++stands2reasonView Answer on Stackoverflow
Solution 9 - C++Mark BView Answer on Stackoverflow