有没有办法查看由编译器(如VC ++ 2008)为未定义它们的类生成的默认函数(例如,默认复制构造函数,默认赋值运算符)?
答案 0 :(得分:11)
使用clang
编译器,您可以通过传递-ast-dump
参数来查看它们。 Clang仍处于开发阶段,但您已经可以将它用于这些事情:
[js@HOST2 cpp]$ cat main1.cpp
struct A { };
[js@HOST2 cpp]$ clang++ -cc1 -ast-dump main1.cpp
typedef char *__builtin_va_list;
struct A {
public:
struct A;
inline A();
inline A(struct A const &);
inline struct A &operator=(struct A const &);
inline void ~A();
};
[js@HOST2 cpp]$
我希望这就是你要求的。让我们改变代码再看一遍。
[js@HOST2 cpp]$ cat main1.cpp
struct M { M(M&); };
struct A { M m; };
[js@HOST2 cpp]$ clang++ -cc1 -ast-dump main1.cpp
typedef char *__builtin_va_list;
struct M {
public:
struct M;
M(struct M &);
inline struct M &operator=(struct M const &);
inline void ~M();
};
struct A {
public:
struct A;
struct M m;
inline A();
inline A(struct A &);
inline struct A &operator=(struct A const &);
inline void ~A();
};
[js@HOST2 cpp]$
注意A
的隐式声明的复制构造函数现在如何具有非const引用参数,因为其中一个成员也具有(成员m
),并且M
没有声明了默认构造函数。
要获取生成的代码,可以让它发出虚拟机中间语言。让我们看一下生成的代码:
struct A { virtual void f(); int a; };
A f() { A a; a = A(); return a; } // using def-ctor, assignment and copy-ctor
[js@HOST2 cpp]$ clang++ -cc1 -O1 -emit-llvm -o - main1.cpp | c++filt
[ snippet ]
define linkonce_odr void @A::A()(%struct.A* nocapture %this) nounwind align 2 {
entry:
%0 = getelementptr inbounds %struct.A* %this, i32 0, i32 0 ; <i8***> [#uses=1]
store i8** getelementptr inbounds ([3 x i8*]* @vtable for A, i32 0, i32 2), i8*** %0
ret void
}
define linkonce_odr %struct.A* @A::operator=(A const&)(%struct.A* %this,
%struct.A* nocapture) nounwind align 2 {
entry:
%tmp = getelementptr inbounds %struct.A* %this, i32 0, i32 1 ; <i32*> [#uses=1]
%tmp2 = getelementptr inbounds %struct.A* %0, i32 0, i32 1 ; <i32*> [#uses=1]
%tmp3 = load i32* %tmp2 ; <i32> [#uses=1]
store i32 %tmp3, i32* %tmp
ret %struct.A* %this
}
define linkonce_odr void @A::A(A const&)(%struct.A* nocapture %this, %struct.A* nocapture)
nounwind align 2 {
entry:
%tmp = getelementptr inbounds %struct.A* %this, i32 0, i32 1 ; <i32*> [#uses=1]
%tmp2 = getelementptr inbounds %struct.A* %0, i32 0, i32 1 ; <i32*> [#uses=1]
%tmp3 = load i32* %tmp2 ; <i32> [#uses=1]
store i32 %tmp3, i32* %tmp
%1 = getelementptr inbounds %struct.A* %this, i32 0, i32 0 ; <i8***> [#uses=1]
store i8** getelementptr inbounds ([3 x i8*]* @vtable for A, i32 0, i32 2), i8*** %1
ret void
}
现在,我不理解中间语言(在llvm.org定义)。但是您可以使用llvm编译器将所有代码转换为C:
[js@HOST2 cpp]$ clang++ -cc1 -O1 -emit-llvm -o - main1.cpp | llc -march=c -o - | c++filt
[snippet]
void A::A()(struct l_struct.A *llvm_cbe_this) {
*((&llvm_cbe_this->field0)) = ((&_ZTV1A.array[((signed int )2u)]));
return;
}
struct l_struct.A *A::operator=(A const&)(struct l_struct.A *llvm_cbe_this, struct l_struct.A
*llvm_cbe_tmp__1) {
unsigned int llvm_cbe_tmp3;
llvm_cbe_tmp3 = *((&llvm_cbe_tmp__1->field1));
*((&llvm_cbe_this->field1)) = llvm_cbe_tmp3;
return llvm_cbe_this;
}
void A::A(A const&)(struct l_struct.A *llvm_cbe_this, struct l_struct.A *llvm_cbe_tmp__2) {
unsigned int llvm_cbe_tmp3;
llvm_cbe_tmp3 = *((&llvm_cbe_tmp__2->field1));
*((&llvm_cbe_this->field1)) = llvm_cbe_tmp3;
*((&llvm_cbe_this->field0)) = ((&_ZTV1A.array[((signed int )2u)]));
return;
}
多田!请注意它如何在复制构造函数和默认构造函数中设置虚拟表指针。希望这可以帮助。
答案 1 :(得分:7)
您可以使用调试器跟踪代码以查看正在进行的操作。例如:
#include <string>
struct A {
int a[100];
char c;
std::string s;
};
int main() {
A a;
A b(a);
}
通过复制构造函数在'b'的构造处设置断点。 VC ++ 6调试器中此时的汇编器输出为:
12: A b(a);
00401195 lea eax,[ebp-1B0h]
0040119B push eax
0040119C lea ecx,[ebp-354h]
004011A2 call @ILT+140(A::A) (00401091)
最后一个是复制构造函数调用。如果您想了解更多细节,也可以追溯到这一点。
但是,如果您的问题是“如何查看复制构造函数的C ++代码等”,答案是您不能,因为没有 - 编译器生成汇编程序或机器代码(取决于你的编译器),而不是C ++代码。
答案 2 :(得分:2)
编译器生成的方法是抽象的,它们在源代码中不存在 请看下面的示例,我尝试解释四个编译器生成的方法应该在源代码级别执行的操作。从这里你应该能够推断出任何正常的类。
如果您有这样的课程:
class X: public Base
{
int* a;
double b;
Y c;
};
然后编译器生成以下内容的等价物:
X::X() // Default constructor
:Base() Calls the base class default constructor
//,a pointers are POD no default initialization
//,b double are POD no default initialization
,c() //Call default constructor on each non POD member
{}
X::~X() // Default destructor
{}
// Destructor for each non POD member in reverse order
~c() calls the destructor of the class type
//~b double are POD no destructor
//~a pointers are POD no destructor
~Base() // Calls the base class destructor
X::X(X const& copy)
:Base(copy) // calls the base class copy constructor
// Copies each member using its copy constructor
,a(copy.a) // Pointers copied (Note just the pointer is copied, not what it points at)
,b(copy.b) // Double copied.
,c(copy.c) // Uses copy constructor of the class type (must be accessible)
{}
X& X::operator=(X const& copy)
{
Base::operator=(copy); // Calls the base class assignment operator
// Copies each member using the members assignment operator
a = copy.a; // Pointers copied (Note just the pointer is copied, not what it points at)
b = copy.b; // Double copied
c = copy.c; // Uses assignment operator of the class type (must be accessible)
return *this;
}
答案 3 :(得分:0)
答案 4 :(得分:-1)
您确定需要查看此功能吗?
默认情况下,编译器通过在每个成员变量上调用Copy Constructor或Assignment运算符来创建它们。
问题在于,当您使用使用引用计数来管理数据的对象时,默认的复制构造函数将创建对象的副本,但不会创建对象指向的数据的副本。这也是指针的情况。所以如果你有一个类:
class aClass
{
int one;
int *ptwo;
};
默认的复制构造函数只复制数据a和指针b。但是他没有复制b指向的数据。如果您使用此类
aClass a, b;
a.ptwo = new int;
a.one = 1;
*(a.ptwo) = 2;
b = a;
*(b.ptwo) = 1;
//a.ptwo now points to an integer with the value of 1
如果您希望此类复制ptwo的值而不是指针,则需要您自己的复制赋值运算符函数。如果您对更多详细信息感兴趣,那么默认函数会执行什么操作以及不执行哪些功能,那么您可以查看“Effective C ++”一书。
它有一整章关于这个东西,它解释了类的默认功能,不执行,应该做什么,何时编写自己的功能。如果您只想了解此功能,我相信您可以在网络上获得数字版本。