目前,我正在阅读Protocol Buffer
的源代码,我发现了一个奇怪的enum
代码here
~scoped_ptr() {
enum { type_must_be_complete = sizeof(C) };
delete ptr_;
}
void reset(C* p = NULL) {
if (p != ptr_) {
enum { type_must_be_complete = sizeof(C) };
delete ptr_;
ptr_ = p;
}
}
为什么在这里定义enum { type_must_be_complete = sizeof(C) };
?它用于什么?
答案 0 :(得分:81)
这个技巧通过确保在编译析构函数时可以使用C的定义来避免UB。否则编译将失败,因为无法确定GLuint programhandle=glCreateProgram();
glAttachShader(programhandle, vertex);
glAttachShader(programhandle, fragment);
glLinkProgram(programhandle);
GLint status;
position=glGetAttribLocation(programhandle,"position");
color=glGetAttribLocation(programhandle, "color");
uniformmatrix=glGetUniformLocation(programhandle, "matrix");
fulltransform=glGetAttribLocation(programhandle, "fulltransform");
glEnableVertexAttribArray(position);
glEnableVertexAttribArray(color);
glEnableVertexAttribArray(fulltransform);
glEnableVertexAttribArray(fulltransform+1);
glEnableVertexAttribArray(fulltransform+2);
glEnableVertexAttribArray(fulltransform+3);
glGetProgramiv(programhandle , GL_LINK_STATUS, &status);
if (status==GL_FALSE)
{
NSLog(@"program");
}
glDeleteShader(vertex);
glDeleteShader(fragment);
glUseProgram(programhandle);
不完整类型(前向声明的类型),但可以使用指针。
在编译的二进制文件中,此代码将被优化掉,并且不起作用。
请注意:从5.3.5 / 5中删除不完整类型可能是未定义的行为:。
如果要删除的对象在此处具有不完整的类类型 删除和完整的类有一个非平凡的析构函数或a 解除分配功能,行为未定义。
sizeof
甚至发出以下警告:
警告:在调用delete时检测到可能的问题 操作员:
警告:'p'类型不完整
警告:前进 声明'struct C'
答案 1 :(得分:31)
sizeof(C)
不是完整类型, C
将在编译时失败。设置本地范围enum
使语句在运行时是良性的。
这是程序员保护自己的一种方式:如果一个不完整类型的后续delete ptr_
具有非平凡的析构函数,则该行为是未定义的。
答案 2 :(得分:28)
要理解enum
,首先要考虑没有它的析构函数:
~scoped_ptr() {
delete ptr_;
}
其中ptr_
是C*
。如果此时类型C
不完整,即编译器知道的所有内容都是struct C;
,那么(1) 默认生成的 - 没有析构函数用于指向的C实例。对于由智能指针管理的对象,这不太可能是正确的做法。
如果通过指向不完整类型的指针进行删除始终具有未定义行为,则标准可能只需要编译器对其进行诊断并失败。但是当真正的析构函数是微不足道的时候它是明确定义的:程序员可以拥有的知识,但编译器没有。为什么语言定义并允许这超出我的范围,但C ++支持许多当前不被视为最佳实践的实践。
完整类型具有已知大小,因此sizeof(C)
将编译,当且仅当C
是完整类型时 - 使用已知的析构函数。所以它可以用作守卫。一种方法就是
(void) sizeof(C); // Type must be complete
我会猜测通过一些编译器和选项,编译器会在它注意到它不应该编译之前对其进行优化,并且enum
是一种避免这种非编译的方法 - 符合编译器行为:
enum { type_must_be_complete = sizeof(C) };
选择enum
而不仅仅是丢弃的表达的另一种解释只是个人偏好。
或者正如James T. Hugget建议in a comment给出这个答案,“枚举可能是一种在编译时创建伪可移植错误消息的方法。”
(1)对于不完整类型,默认生成的do-nothing析构函数是旧std::auto_ptr
的问题。由国际C ++标准化委员会主席Herb Sutter撰写的a GOTW item about the PIMPL idiom进入{{3}}是如此阴险。当然,现在不推荐使用std::auto_ptr
,而是使用其他机制。
功能
答案 3 :(得分:3)
可能确保定义C
的技巧。