2010-05-13 55 views
9

两个引号++标准,§1.8:空基类优化从C

一个目的是存储的区域。

基类子对象的大小可能为零。

我不认为一个存储区域的大小可以是零。这意味着某些基类子对象实际上不是对象。这些陈述如何共存?

+1

为什么你认为一个存储区域的大小不能为零?显然它可以:) – 2010-05-13 12:24:36

+1

调用malloc(0)。 – bmargulies 2010-05-13 12:27:48

+1

@bmargulies'malloc'在C语言中特别措辞,因此它不需要创建一个零大小的对象:“如果请求的空间大小为零,则行为是实现定义的:或者返回空指针,或者行为就好像大小是非零值“ – 2010-05-13 12:37:50

回答

10

关于“区域”定义的哲学论证是不必要的。 “除非是位域,否则大多数派生对象的大小应该不为零......基类子对象的大小可能为零”。

因此,标准很清楚什么对象(以及哪些“存储区域”)可以具有零大小。如果你不同意这个标准,“区域”在英语中意味着什么是一回事,你可以对作者的(与编程无关的)文学技能作出错误的判断。对于这个问题,你可以错过他们的诗歌技巧(14.7.3/7)。但是,这个标准在这里所说的关于类类型对象的大小很清楚。

阅读标准的实用方法是,如果给出两个合理的单词解释,选择一个与标准的同一部分中的另一个句子不直接相抵触的单词。不要选择更贴近您个人喜欢使用的单词,甚至是最常见的用法。

+0

“除非是位域,否则大多数派生对象应该有一个非零大小”似乎与被称为“分配一个没有元素的数组”的new int [0]冲突。这意味着在这种情况下数组实际上* do *包含内部填充吗? – 2010-05-13 12:45:45

+0

我不认为数组是一个派生最多的对象。 1.8/4:“如果一个完整的对象... ...是类类型,它的类型被认为是最派生类......最派生类类型的对象被称为最派生类对象” – 2010-05-13 12:47:48

+0

@Steve它说“大多数派生类类型或非类类型的对象称为最派生对象“。 - 编辑:C++ 03的措辞似乎与FCD措辞不同。 – 2010-05-13 12:50:19

9

C++不允许大小为零的对象,因为每个对象都必须具有唯一的内存地址。所以,如果您有:

struct empty {}; 

// ... 

empty myempty; 
empty* ptr = &myempty; 

然后ptr必须指向一个唯一的内存地址。该标准规定为此目的,对象的最小尺寸为1个字节。类似地,允许分配大小0,并返回一个有效的指针,即使不允许写入该指针(这适用于malloc(0),并且new empty返回指向一个字节的指针,因为sizeof(empty) == 1)。

如果我从empty得到像这样:

struct derived : public empty 
{ 
    int data; 
} 

不再有基类empty占用一个字节的任何一点,因为所有derived都会有一个唯一的地址,由于data成员。在这种情况下,引用“基类子对象可能具有零大小”以允许编译器不为empty使用任何空间,例如sizeof(derived) == 4。正如标题所述,这只是一种优化,对derivedempty部分占用零空间是完全合法的。

+0

实际上'sizeof(空)'被允许更大,并且大多数编译器至少会使它的机器字宽。 – 2010-05-13 13:18:53

+0

“每个对象都必须具有唯一的内存地址” - 尽管标准中的表述不正确,但每个对象都不需要具有唯一的内存地址。一个对象可以在其开始时与子对象具有相同的地址,和/或基类子对象。应该说每个_complete_对象都必须有一个唯一的地址。 – davmac 2016-06-04 21:49:54

2

C++标准1.8.5状态: -

除非它是一个位字段(9.6),一个最 派生对象应具有非零 尺寸,并应占据一个或多个 存储字节。 基类 子对象可能具有零大小。一个可以复制的对象 或 标准布局类型(3。9) 占用连续的存储字节。

因此,该标准允许没有数据成员(并且没有虚拟)的基类与另一个具有不同类型的子对象共享相同的地址。您可以玩像空基类大小...

struct a{}; 
struct a1{}; 
struct b : public a, public a1{char c;}; 


int main() 
{ 
    std::cout << sizeof(b) << "\n"; 
    std::cout << sizeof(b::a); 
} 


Which outputs (ignoring padding)... 

1 
1 


now try: 


struct a{}; 
struct b : public a {a ax;}; 


int main() 
{ 
    std::cout << sizeof(b) << "\n"; 
    std::cout << sizeof(b::a); 
} 


and the output is ... 

2 
1 

因为一个(作为基础和作为成员)的两个实例必须有不同的地址。

BTW:“b :: a”是另一种说“a”的方式。范围访问操作符的存在不会请求“类型a的b的基类子对象”。 5.3.3/2说: - 将sizeof应用于基类子对象时,结果为该对象的类型为的大小。