在C++14标准(C++98/11也一样)中,在Annex C Compatibility 里有这么一条:
Change: Converting void* to a pointer-to-object type requires casting
1 2 3 4 5 char a[10 ];void * b=a;void foo () { char * c=b; }
ISO C will accept this usage of pointer to void being assigned to a pointer to object type. C ++ will not.
但是为什么operator new()
会返回void*
且不用显式转换为T*
就能赋值给T*
呢?
global scope 的operator new
基本原型为:
1 2 void * operator new ( std::size_t count ) ;void * operator new []( std::size_t count );
这么写不会有任何警告:
1 2 3 struct A {};A* ap=new A; delete ap;
自己在类内重载一个operator new
返回void*
也不会有问题:
1 2 3 4 5 6 struct A { void * operator new (std::size_t x) { return malloc (x); } }; A* ap=new A; delete ap;
但是使用std::malloc
来分配再赋值就有问题:
1 2 A* ap=std::malloc (sizeof (A));
编译器也是支持C++14
标准的Clang 3.9
…
我通过使用LLVM-IR分析发现,调用operator new
时编译器会自动将void*
转换为T*
:
1 2 3 4 5 6 7 struct A {};int main () { A *z=new A; delete z; }
其LLVM-IR为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 define i32 @main () #4 { %1 = alloca i32, align 4 %2 = alloca %struct .A*, align 8 store i32 0 , i32* %1 , align 4 %3 = call i8* @_Znwy(i64 1 ) #7 %4 = bitcast i8* %3 to %struct .A* store %struct .A* %4 , %struct .A** %2 , align 8 %5 = load %struct .A*, %struct .A** %2 , align 8 %6 = icmp eq %struct .A* %5 , null br i1 %6 , label %9 , label %7 ; <label>:7 : ; preds = %0 %8 = bitcast %struct .A* %5 to i8* call void @_ZdlPv(i8* %8 ) #8 br label %9 ; <label>:9 : ; preds = %7 , %0 %10 = load i32, i32* %1 , align 4 ret i32 %10 } ; Function Attrs: nobuiltin declare noalias i8* @_Znwy(i64) #5 ; Function Attrs: nobuiltin nounwind declare void @_ZdlPv(i8*) #6
其中最重要的就是%4
的操作:
1 2 3 4 5 6 # 调用new 获得内存地址 %3 = call i8* @_Znwy(i64 1 ) #7 # 将上一步获得的地址转换为struct .A* %4 = bitcast i8* %3 to %struct .A* # 将转换后的地址存储为我们声明的指针 store %struct .A* %4 , %struct .A** %2 , align 8
经过测试,自己在类内重载operator new
也会具有转换。
1 2 3 4 5 6 7 8 9 struct A { void * operator new (std::size_t x) { return malloc (x); } }; int main () { A *z=new A; delete z; }
其LLVM-IR代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 define i32 @main () #4 { %1 = alloca i32, align 4 %2 = alloca %struct .A*, align 8 store i32 0 , i32* %1 , align 4 %3 = call i8* @_ZN1AnwEy(i64 1 ) %4 = bitcast i8* %3 to %struct .A* store %struct .A* %4 , %struct .A** %2 , align 8 %5 = load %struct .A*, %struct .A** %2 , align 8 %6 = icmp eq %struct .A* %5 , null br i1 %6 , label %9 , label %7 ; <label>:7 : ; preds = %0 %8 = bitcast %struct .A* %5 to i8* call void @_ZdlPv(i8* %8 ) #6 br label %9 ; <label>:9 : ; preds = %7 , %0 %10 = load i32, i32* %1 , align 4 ret i32 %10 } ; Function Attrs: uwtable define linkonce_odr i8* @_ZN1AnwEy(i64) #0 comdat align 2 { %2 = alloca i64, align 8 store i64 %0 , i64* %2 , align 8 %3 = load i64, i64* %2 , align 8 %4 = call i8* @malloc (i64 %3 ) ret i8* %4 } ; Function Attrs: nobuiltin nounwind declare void @_ZdlPv(i8*) #5 declare i8* @malloc (i64) #1
可以看到%4
处仍然具有void*
到struct.A*
的转换。 我没有在标准中找到operator new
应该会转换void*
到T*
类型的显式要求,但是标准中提到了下面这样的描述:
The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).