Подтвердить что ты не робот

Почему у С++ нет виртуальных переменных?

Это может быть задано в миллион раз раньше или может быть невероятно глупым, но почему это не реализовано?

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?

4b9b3361

Ответ 1

Для доступа к 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 не являются полиморфными.)

Что касается того, почему С++ не имеет виртуальных переменных: виртуальные функции допускают полиморфизм; другими словами, они позволяют рассматривать классы двух разных типов одинаково, вызывая код, при этом любые различия во внутреннем поведении этих двух классов инкапсулируются внутри виртуальных функций.

Виртуальные переменные-члены не имеют смысла; нет никакого поведения для инкапсуляции при простом доступе к переменной.

Также имейте в виду, что С++ является статически типизированным. Виртуальные функции позволяют изменять поведение во время выполнения; ваш примерный код пытается изменить не только поведение, но типы данных во время выполнения (A::ais int, B::a is float), а С++ не работает таким образом. Если вам нужно разместить разные типы данных во время выполнения, вам необходимо инкапсулировать эти различия в виртуальных функциях, которые скрывают различия в типах данных. Например (только для демонстрационного кода, для реального кода, вы оператор перегрузки < < вместо):

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

Также имейте в виду, что включение переменных-членов таким образом может нарушить инкапсуляцию и, как правило, неодобрительно.

Ответ 2

Не публиковать данные и получать доступ к ним через виртуальные функции.

Посмотрите на мгновение, как то, что вы просите, должно быть реализовано. В принципе, это заставит любой доступ к любому члену данных пройти виртуальную функцию. Помните, что вы получаете доступ к данным через указатель на объект A, а класс A не знает, что вы делали в классе B.

Другими словами, мы могли бы сделать доступ к любому элементу данных куда угодно медленнее - или вы могли бы написать виртуальный метод. Угадайте, какие дизайнеры С++ выбрали.

Ответ 3

Вы не можете этого сделать, а С++ не поддерживает его, потому что он ломается с фундаментальными принципами С++.

A float - это другой тип, чем int, и поиск имени, а также определение того, какие преобразования потребуются для назначения значений, происходит во время компиляции. Однако то, что действительно названо 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);

Ответ 4

(dynamic_cast<B*>(a))->a? Зачем вам это нужно? Не достаточно ли виртуальных функций?

Ответ 5

Оставляя в стороне аргумент, что виртуальные методы должны быть частными, виртуальные методы предназначены как дополнительный уровень инкапсуляции (инкапсулирующие вариации в поведении), Прямой доступ к полям идет против инкапсуляции, чтобы начать с этого, было бы немного лицемерно создавать виртуальные поля. И поскольку поля не определяют поведение, они просто хранят данные, на самом деле нет никакого поведения для виртуализации. Сам факт, что у вас есть открытый int или float, является анти-шаблоном.

Ответ 6

Вы можете отключить переменную для доступа к B::a.

Что-то вроде:

((B*)a)->a

Я думаю, что это то же самое в большинстве языков программирования OO. Я не могу представить ни одного, реализующего концепцию virtual variables...

Ответ 7

Вы можете создать такой эффект следующим образом:

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

Обратите внимание, что по соображениям скорости я избегал этого.

Ответ 8

Это не поддерживается С++, поскольку это нарушает принципы инкапсуляции.

Ваши классы должны раскрывать и реализовывать общедоступный (возможно, виртуальный) интерфейс, который не говорит пользователям классов о внутренней работе вашего класса. Интерфейс должен описывать операции (и результаты), которые класс может выполнять на абстрактном уровне, а не как "установить эту переменную в X".

Ответ 9

Поскольку согласно стандарту C смещение поля внутри класса или структуры должно быть константой времени компиляции. Это также относится к обращению к полям базового класса.

Ваш пример не будет работать с виртуальными геттерами, поскольку для переопределения требуется подпись того же типа. Если это было необходимо, ваш виртуальный получатель должен был бы вернуться с алгебраическим типом, и код приема должен был бы проверяться во время выполнения, если он был ожидаемого типа.