Overhead of creating a new class
If I ha开发者_Go百科ve a class defined as such:
class classWithInt
{
public:
classWithInt();
...
private:
int someInt;
...
}
and that someInt
is the one and only one member variable in classWithInt
, how much slower would it be to declare a new instance of this class than to just declare a new integer?
What about when you have, say 10 such integers in the class? 100?
With a compiler not written by drunk college students in the wee hours of the morning, the overhead is zero. At least until you start putting in virtual
functions; then you must pay the cost for the virtual dispatch mechanism. Or if you have no data in the class, in which case the class is still required to take up some space (which in turn is because every object must have a unique address in memory).
Functions are not part of the data layout of the object. They are only a part of the mental concept of the object. The function is translated into code that takes an instance of the object as an additional parameter, and calls to the member function are correspondingly translated to pass the object.
The number of data members doesn't matter. Compare apples to apples; if you have a class with 10 ints in it, then it takes up the same space that 10 ints would.
Allocating things on the stack is effectively free, no matter what they are. The compiler adds up the size of all the local variables and adjusts the stack pointer all at once to make space for them. Allocating space in memory costs, but the cost will probably depend more on the number of allocations than the amount of space allocated.
well, lets just test it all out. I can compile, with full optimizations, a more complete example like so:
void use(int &);
class classWithInt
{
public:
classWithInt() : someInt(){}
int someInt;
};
class podWithInt
{
public:
int someInt;
};
int main() {
int foo;
classWithInt bar;
podWithInt baz;
use(foo);
use(bar.someInt);
use(baz.someInt);
return 5;
}
And this is the output I get from gcc-llvm
; ModuleID = '/tmp/webcompile/_21792_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"
%struct.classWithInt = type { i32 }
define i32 @main() {
entry:
%foo = alloca i32, align 4 ; <i32*> [#uses=1]
%bar = alloca %struct.classWithInt, align 8 ; <%struct.classWithInt*> [#uses=1]
%baz = alloca %struct.classWithInt, align 8 ; <%struct.classWithInt*> [#uses=1]
%0 = getelementptr inbounds %struct.classWithInt* %bar, i64 0, i32 0 ; <i32*> [#uses=2]
store i32 0, i32* %0, align 8
call void @_Z3useRi(i32* %foo)
call void @_Z3useRi(i32* %0)
%1 = getelementptr inbounds %struct.classWithInt* %baz, i64 0, i32 0 ; <i32*> [#uses=1]
call void @_Z3useRi(i32* %1)
ret i32 5
}
declare void @_Z3useRi(i32*)
There are some differences in each case. In the simplest case, the POD type differs from the plain int in only one way, it requires a different alignment, it's 8 byte aligned instead of just 4 byte.
The other noticeable thing is that the POD and bare int do not get initialized. Their storage is used right as is in from the wilderness of the stack. The non-pod type, which has a non-trivial constructor, causes a zero to be stored before the instance can be used for anything else.
精彩评论