虚拟类的非虚拟版本

时间:2011-09-26 02:35:43

标签: c++ virtual c++11

假设我们有以下两个类定义。

#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 个答案:

答案 0 :(得分:2)

  

如果你知道它不会被使用,有没有办法在没有虚拟指针的情况下创建B类型的对象?

没有。物体就是它们。虚拟对象总是虚拟的。

毕竟,你可以这样做:

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

这是完全合法和合法的代码。而且C ++必须允许它。类型B是虚拟的,并且具有一定的大小。根据使用的位置,您不能将类型设置为不同的类型。

答案 1 :(得分:2)

在这里回答我自己的问题,但我发现以下工作是通过将A分成虚拟和非虚拟组件来完成的:

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

重要的是,在B<>中不要将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 :(得分:0)

这将是一个很好的,但我想不出任何方法来撤销虚拟成员或避免存储虚拟指针。

你可以通过保持一个没有虚拟指针的大小为B的缓冲区来做一些讨厌的黑客攻击,然后使用强制转换等等。但是所有未定义的行为和平台相关。

答案 3 :(得分:0)

不幸的是,它不会在任何正常意义上工作,因为方法调用中的代码需要虚拟指针位于类定义中。

我想你可以将所有A和B的代码复制/粘贴到一个新类中,但这很快就会成为一个维护问题。