Is there any way to view the default functions ( e.g., default copy constructor, default assignment operator ) generated by a compiler such as VC++2008 for a class which does not define them?
An object-viewing tool (like objdump or dumpbin) can disassemble the output object files for you. Then you can poke around and see what instructions are getting emitted for the functions/methods you care about.
You can trace into the code with the debugger to see what is going on. For example:
#include <string>
struct A {
int a[100];
char c;
std::string s;
};
int main() {
A a;
A b(a);
}
Set a breakpoint at the construction of 'b' by the copy constructor. The assembler output at that point in the VC++6 debugger is:
12: A b(a);
00401195 lea eax,[ebp-1B0h]
0040119B push eax
0040119C lea ecx,[ebp-354h]
004011A2 call @ILT+140(A::A) (00401091)
The last is the copy constructor call. You can trace into that too if you want more details.
However, if your question is "how can I see the C++ code for the copy constructor et al", the answer is you can't, because there isn't any - the compiler generates assembler or machine code (depending on your compiler) for them, not C++ code.
With the clang
compiler, you can see them by passing the -ast-dump
argument. Clang is still in development stage, but you can already use it for these things:
[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]$
I hope that's what you asked for. Let's change the code and look again.
[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]$
Notice how the implicitly declared copy constructor of A
now has a non-const reference parameter, because one of its members has too (member m
), and that M
has no default constructor declared.
For getting the generated code, you can let it emit virtual machine intermediate language. Let's look on the generated code for this:
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
}
Now, i don't understand that intermediate language (which is defined at llvm.org). But you can translate all that code into C using the llvm compiler:
[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;
}
Tada! Notice how it sets the virtual table pointer in the copy constructor and default constructor. Hope this helps.
Are you sure you need to view this functions?
Per default the Compiler creates them by calling the Copy Constructor or Assignment operator on each member Variable.
The Problem with that is that when you use Objects which use reference Counting for managing data the default Copy Constructor will create a Copy of the Object but not a copy of the Data the Object points to. This also is the case for pointers. So if your have a class like:
class aClass
{
int one;
int *ptwo;
};
The default Copy constructor only copy's the data a and the pointer b. However he does not copy the data pointed to by b. If you use this class like
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
If you want this Class to copy the value of ptwo instead of the pointer you would need your own copy assignment operator function. If you are interested in more details what the default functions do and what not then you take a look into the Book "Effective C++".
It has a whole chapter on this stuff, which explains what default functions of classes do, don't do, what they should do, when to write your own. I'm sure you can get a digital version on the web if you just want to know about this functionality.
The compiler generated methods are abstract they don't exist in source code.
Look at the example below, I try and explain what the four compiler generated methods are supposed to do at the source code level. From this you should be able to extrapolate any normal class.
If you have a class like this:
class X: public Base
{
int* a;
double b;
Y c;
};
Then the compiler generates the equivalent of the following:
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;
}