C++的trivial destructor和value initialization

一、标量类型的析构

下面是一种简化的模型,实际项目中通常对应着缓存区结构,可能会主动调用析构函数,但是这种类型不排除有标量类型的情况。下面的例子中是int类型,但是事实上可能更多的是一种指针类型,那么指针类型主动调用析构会清零吗?
tsecer@harry: cat exp.call.dtor.cpp
template <typename T>
struct A
{
T t;
~A() { t.~T();}
};
int main()
{
A<int> a;
return 0;
}
tsecer@harry: g++ -g exp.call.dtor.cpp
tsecer@harry: gdb a.out
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /data/home/harry/work/exp.call.dctor/a.out...done.
(gdb) disas A<int>::~A
Dump of assembler code for function A<int>::~A():
0x00000000004005d4 <+0>: push %rbp
0x00000000004005d5 <+1>: mov %rsp,%rbp
0x00000000004005d8 <+4>: mov %rdi,-0x8(%rbp)
0x00000000004005dc <+8>: pop %rbp
0x00000000004005dd <+9>: retq
End of assembler dump.
(gdb)
从汇编代码看,析构中没有执行任何额外操作。

二、gcc规范中对于这种内置类型的说明

C++规范并没有明确说明这种标量类型如何析构,但是在析构函数中规定了trivial destructor的概念,并且明确说明了对于trivial类析构函数不需要执行任何操作:“A trivial destructor is a destructor that performs no action.”
Trivial destructor
The destructor for class T is trivial if all of the following is true:

The destructor is not user-provided (meaning, it is either implicitly declared, or explicitly defined as defaulted on its first declaration)
The destructor is not virtual (that is, the base class destructor is not virtual)
All direct base classes have trivial destructors
All non-static data members of class type (or array of class type) have trivial destructors
A trivial destructor is a destructor that performs no action. Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage. All data types compatible with the C language (POD types) are trivially destructible.

三、gcc中对这个代码的处理


static void
one_static_initialization_or_destruction (tree decl, tree init, bool initp)
{
tree guard_if_stmt = NULL_TREE;
tree guard;

/* If we are supposed to destruct and there's a trivial destructor,
nothing has to be done. */
if (!initp
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
return;
……
}

四、例子

tsecer@harry: cat exp.call.dtor.cpp
template <typename T>
struct A
{
T t;
~A() { t.~T();}
};
int main()
{
A<int> a;
return 0;
}
tsecer@harry: g++ -g exp.call.dtor.cpp
tsecer@harry: gdb a.out
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /data/home/harry/work/exp.call.dctor/a.out...done.
(gdb) disas A<int>::~A
Dump of assembler code for function A<int>::~A():
0x00000000004005d4 <+0>: push %rbp
0x00000000004005d5 <+1>: mov %rsp,%rbp
0x00000000004005d8 <+4>: mov %rdi,-0x8(%rbp)
0x00000000004005dc <+8>: pop %rbp
0x00000000004005dd <+9>: retq
End of assembler dump.

五、构造函数呢

1、C++手册的说明

8.5 Initializers
5 To zero-initialize an object or reference of type T means:
— if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression,
converted to T;103
— if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class
subobject is zero-initialized and padding is initialized to zero bits;
— if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zeroinitialized
and padding is initialized to zero bits;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.
……
7 To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no accessible default
constructor);
— if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is
called.
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized.
An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International
Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc.,
even if no constructor is invoked for the object’s initialization.
……
这里说的就是常规的带有空括号的形式
10 An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
[ Note: Since () is not permitted by the syntax for initializer,
X a();
is not the declaration of an object of class X, but the declaration of a function taking no argument and
returning an X. The form () is permitted in certain other initialization contexts (5.3.4, 5.2.3, 12.6.2). —end
note ]
11 If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an
object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or
thread storage duration are zero-initialized, see 3.6.2. —end note ]

2、举个栗子

tsecer@harry: cat value.initialize.cpp
struct A
{
long intarr[0x11];
};

struct B
{
int intarr[0x22];
B():intarr(){}
};

int main(int argc, const char * argv[])
{
A a;
//A aa();//this is a function declare with return type as A and takes no parameter
B b;
return A().intarr[0x10];
}
tsecer@harry: g++ -g value.initialize.cpp
tsecer@harry: gdb ./a.out
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /data/home/harry/work/exp.call.dctor/a.out...done.
(gdb) disas main
Dump of assembler code for function main(int, char const**):
0x00000000004005b0 <+0>: push %rbp
0x00000000004005b1 <+1>: mov %rsp,%rbp
0x00000000004005b4 <+4>: sub $0x130,%rsp
0x00000000004005bb <+11>: mov %edi,-0x124(%rbp)
0x00000000004005c1 <+17>: mov %rsi,-0x130(%rbp)
0x00000000004005c8 <+24>: lea -0x120(%rbp),%rax
0x00000000004005cf <+31>: mov %rax,%rdi
0x00000000004005d2 <+34>: callq 0x4005f8 <B::B()>
0x00000000004005d7 <+39>: lea -0x90(%rbp),%rsi
0x00000000004005de <+46>: mov $0x0,%eax
0x00000000004005e3 <+51>: mov $0x11,%edx
0x00000000004005e8 <+56>: mov %rsi,%rdi
0x00000000004005eb <+59>: mov %rdx,%rcx
0x00000000004005ee <+62>: rep stos %rax,%es:(%rdi)这里将所有%edx 中的$0x11个long都初始化为%eax寄存器中的值,也即是前一条指令中设置的$0x0
0x00000000004005f1 <+65>: mov -0x10(%rbp),%rax
0x00000000004005f5 <+69>: leaveq
0x00000000004005f6 <+70>: retq
End of assembler dump.
(gdb) disas B::B
Dump of assembler code for function B::B():
0x00000000004005f8 <+0>: push %rbp
0x00000000004005f9 <+1>: mov %rsp,%rbp
0x00000000004005fc <+4>: mov %rdi,-0x8(%rbp)
0x0000000000400600 <+8>: mov -0x8(%rbp),%rax
0x0000000000400604 <+12>: mov $0x21,%edx//这里通过循环将0x22个int初始化为零
0x0000000000400609 <+17>: jmp 0x400619 <B::B()+33>
0x000000000040060b <+19>: movl $0x0,(%rax)
0x0000000000400611 <+25>: add $0x4,%rax
0x0000000000400615 <+29>: sub $0x1,%rdx
0x0000000000400619 <+33>: cmp $0xffffffffffffffff,%rdx
0x000000000040061d <+37>: jne 0x40060b <B::B()+19>
0x000000000040061f <+39>: pop %rbp
0x0000000000400620 <+40>: retq
End of assembler dump.
(gdb)

posted on 2020-04-27 20:36  tsecer  阅读(564)  评论(0编辑  收藏  举报

导航