Не виртуальная версия виртуального класса - PullRequest
3 голосов
/ 26 сентября 2011

Допустим, у нас есть следующие два определения класса.

#include <iostream>
#include <array>

class A
{
public:
  virtual void f() = 0;
};

class B : public A
{
public:
  virtual void f() { std::cout << i << std::endl; }
  int i;
};

Здесь sizeof(B) == 8, предположительно 4 виртуальных указателя и 4 для int.

Теперь предположим, что мы создаем массив из B, например:

std::array<B, 10> x;

Теперь мы получаем sizeof(x) == 80.

Если мое понимание верно, все вызовы методов для элементов x разрешаются статически, так как мы знаем тип во время компиляции. Если мы не сделаем что-то вроде A* p = &x[i], я не вижу необходимости даже хранить виртуальный указатель.

Есть ли способ создать объект типа B без виртуального указателя, если вы знаете, что он не будет использоваться?

т.е. тип шаблона nonvirtual<T>, который не содержит виртуального указателя и на который нельзя указать подтипом T? ​​

Ответы [ 4 ]

2 голосов
/ 26 сентября 2011

Отвечая на мой собственный вопрос здесь, но я обнаружил, что следующее выполняет работу, разделив А на его виртуальные и не виртуальные компоненты:

enum is_virtual
{
  VIRTUAL,
  STATIC
};

template <is_virtual X>
class A;

template<>
class A<STATIC>
{
};

template<>
class A<VIRTUAL> : public A<STATIC>
{
public:
  virtual void f() = 0;
  virtual ~A() {}
};

template <is_virtual X>
class B : public A<X>
{
public:
  void f() { std::cout << i << std::endl; }
  int i;
};

Здесь важно то, что1004 * не указывайте f() как виртуальный.Таким образом, он будет виртуальным, если класс наследует A<VIRTUAL>, но не будет виртуальным, если он наследует A<STATIC>.Тогда мы можем сделать следующее:

int main()
{
  std::cout << sizeof(B<STATIC>) << std::endl; // 4
  std::cout << sizeof(B<VIRTUAL>) << std::endl; // 8
  std::array<B<STATIC>, 10> x1;
  std::array<B<VIRTUAL>, 10> x2;
  std::cout << sizeof(x1) << std::endl; // 40
  std::cout << sizeof(x2) << std::endl; // 80
}
2 голосов
/ 26 сентября 2011

Есть ли способ создать объект типа B без виртуального указателя, если вы знаете, что он не будет использоваться?

Нет. Объекты - это то, что они есть. Виртуальный объект виртуален, всегда.

В конце концов, вы могли бы сделать это:

A *a = &x[2];
a->f();

Это совершенно законный и законный кодекс. И C ++ должен позволять это. Тип B является виртуальным и имеет определенный размер. Вы не можете сделать тип другим типом в зависимости от того, где он используется.

0 голосов
/ 26 сентября 2011

К сожалению, это не будет работать в обычном смысле, поскольку код внутри вызовов метода ожидает, что виртуальный указатель будет в определении класса.

Полагаю, вы можете скопировать / вставить весь код А и В. в новый класс, но это быстро станет головной болью при обслуживании.

0 голосов
/ 26 сентября 2011

Это было бы неплохо, но я не могу придумать, как отозвать виртуальных членов или не хранить виртуальный указатель.

Возможно, вы могли бы сделать несколько неприятных хаков, сохранив буферразмер B без виртуального указателя, и играть с приведениями и тому подобное.Но все это неопределенное поведение и зависит от платформы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...