Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


c++ oo syntax behind the scenes behavior


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
22 replies to this topic

#1 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 09:43 AM

if i declare a class, then declare an instance of that class, when is the instance allocated on the heap (assuming it has member variables) ?

 

where is the VMT stored, and when ?

 

and there's just one VMT per class, right?

 

non virtual method calls require no vmt lookup, correct?

 

 

 

 


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


Sponsor:

#2 Servant of the Lord   Crossbones+   -  Reputation: 20974

Like
5Likes
Like

Posted 16 April 2014 - 10:01 AM

if i declare a class, then declare an instance of that class, when is the instance allocated on the heap (assuming it has member variables)?

The instance is allocated on the heap when you call new (or malloc() or another similar func). No sooner, no later.
 

where is the VMT stored, and when ?

With the memory of the class, when the class is created - usually alongside the member variables in memory. Sorry, not thinking clearly. The pointer to the v-table is stored alongside the member variables (because it is a member variable - just a hidden one added by the compiler).
 

and there's just one VMT per class, right?

One VMT per class instance, yes.

[Edit:] I forgot about multiple inheritance. One VMT for each base class in each instance.
 

class Derived : BaseA, BaseB

One v-table for BaseA, one for BaseB.

 

[Edit 2:] Wow, really not thinking straight today. Read TheComet's post below. happy.png Each class definition will have it's own v-table, but each class instance just has a unique pointer to a v-table. BaseA and BaseB each have a pointer to a v-table. BaseA points to BaseA's v-table, but Derived inherits that pointer and changes it to point to Derived's custom BaseA v-table. BaseB has a pointer to BaseB's v-table, but Derived inherits that and changes the value to point to Derived's custom BaseB v-table.

 

non virtual method calls require no vmt lookup, correct?

Yes. And if the class itself has zero virtual functions, then it won't have any Virtual Method Table.

Wikipedia gives some examples here.


Edited by Servant of the Lord, 16 April 2014 - 10:37 AM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#3 TheComet   Crossbones+   -  Reputation: 1640

Like
3Likes
Like

Posted 16 April 2014 - 10:06 AM

When you define/instantiate an instance of a class using the new operator, sizeof(ClassName) number of bytes are allocated on the heap, and a pointer pointing to the beginning of the instance is returned.

ClassName* instance = new ClassName(); // memory is allocated on the heap here
instance->doSomething();

When you define/instantiate an instance of a class on the stack, the memory is guaranteed to be allocated when you need it.

ClassName instance; // Memory is allocated on the stack here
instance.doSomething();

The VMT is typically stored by adding an extra private pointer member variable to the class, which points to a virtual table. Every instance of the class generally shares the same virtual table.

 

If you add more virtual functions, the class size does not grow though.

 

This can be shown with the following code:

#include <iostream>

class Foo
{
public:
    Foo() {}
    virtual ~Foo() {}
private:
    int a;
};

class Bar
{
public:
    Bar() {}
    virtual ~Bar() {}
    virtual void set( int a ) { this->a = a; }
    virtual int get( int a ) { return a; }
private:
    int a;
};

int main()
{
    std::cout << "sizeof(Foo): " << sizeof(Foo) << std::endl;
    std::cout << "sizeof(Bar): " << sizeof(Bar) << std::endl;

    return 0;
}

 

Foo and Bar have the same size, even though Bar has more virtual functions.


YOUR_OPINION >/dev/null

#4 SiCrane   Moderators   -  Reputation: 9662

Like
5Likes
Like

Posted 16 April 2014 - 10:28 AM

 

where is the VMT stored, and when ?

With the memory of the class, when the class is created - usually alongside the member variables in memory.
 

I think you're confusing a pointer to the vtable and the vtable itself. An object will store a pointer to the vtable not the full table. The vtables themselves are generally stored in a read only data segment. 



#5 Bregma   Crossbones+   -  Reputation: 5394

Like
4Likes
Like

Posted 16 April 2014 - 10:29 AM


If you add more virtual functions, the class size does not grow though.

That can be a little misleading.  The size of objects (class instances) does not grow, but the vtable size changes, and maybe even its layout changes.  That's important to know, because it's an ABI change and the not infrequent cause of subtle and inexplicable runtime failures.

 

This sort of thing shows up as a problem if, for example, you add a virtual function to a header file and rebuild a program, but do not rebuild a DLL that also uses the header file.  Bam!


Stephen M. Webb
Professional Free Software Developer

#6 Servant of the Lord   Crossbones+   -  Reputation: 20974

Like
0Likes
Like

Posted 16 April 2014 - 10:35 AM

I think you're confusing a pointer to the vtable and the vtable itself. An object will store a pointer to the vtable not the full table. The vtables themselves are generally stored in a read only data segment. 

Yep, already edited my post. I should remember to hold off on posting in the morning until after breakfast. laugh.png


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#7 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 11:01 AM


The instance is allocated on the heap when you call new (or malloc() or another similar func). No sooner, no later.

 

what about something like:

 

class myclass
etc
 
 
 
void main
{
myclass object1;   // declare an instance of a myclass object called object1
etc
}
 

 

note that my syntax may be off a bit there, but i think you get the type of declaration i'm talking about.

 

that would get allocated when main was called, right?

 

but something like....

 

 
class myclass
etc
 
myclass object1;   // declare an instance of a myclass object called object1, global to everything that follows in the module.
 
 
void main
{
etc
}
 

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#8 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 11:06 AM


When you define/instantiate an instance of a class on the stack, the memory is guaranteed to be allocated when you need it.
ClassName instance; // Memory is allocated on the stack here
instance.doSomething();

 

fascinating! i thought local declarations did a new in the background and a dispose on return, using the heap.

 

so locally declared objects go on the stack. that would imply no memory leaks possible, correct?


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#9 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 11:07 AM


This sort of thing shows up as a problem if, for example, you add a virtual function to a header file and rebuild a program, but do not rebuild a DLL that also uses the header file.  Bam!

 

the classic code and header out of sync explosion!


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#10 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 11:14 AM

based on the above posts, given:

 

 
class myclass
etc
 
void main
{
myclass object1;
}
 

 

would object1 go on the stack when main was called? or is main a special case and it goes in the data segment?

 

also, i takes it that given:

 

 
class myclass
etc
 
myclass object1;  // global to code that follows in this module
 
void main
{
etc
}
 
 

 

 

object1 (its member var storage space) would go in the data segment?


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#11 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 11:36 AM


 
Quote
non virtual method calls require no vmt lookup, correct?
Yes. And if the class itself has zero virtual functions, then it won't have any Virtual Method Table.

 

 

 

I think you're confusing a pointer to the vtable and the vtable itself. An object will store a pointer to the vtable not the full table. The vtables themselves are generally stored in a read only data segment. 

Yep, already edited my post. I should remember to hold off on posting in the morning until after breakfast. laugh.png

 

 

all this seems to imply that all method calls require dereferencing the object's vmt pointer to get the jump address of the actual method to use. i thought non-virtual methods had their jump addresses stored in the objects, to avoid the derferencing overhead.

 

so all method calls become: gosub vmt_ptr->method->address ?  

 

IE gosub to the address of <method> stored in the vmt pointed to by vmt_ptr ?

 

for non-virtual, I thought it was more like: gosub instance.method_address[method]

 

and a stand alone function, by comparison is simply: gosub method_address, correct?  


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#12 Pink Horror   Members   -  Reputation: 1228

Like
4Likes
Like

Posted 16 April 2014 - 11:57 AM


for non-virtual, I thought it was more like: gosub instance.method_address[method]
 
and a stand alone function, by comparison is simply: gosub method_address, correct?  

 

A non-virtual member function and a stand-alone function are pretty much the same thing. The method address is known after linking. The only real difference is the implicit "this" argument.



#13 nobodynews   Crossbones+   -  Reputation: 2099

Like
1Likes
Like

Posted 16 April 2014 - 12:58 PM

fascinating! i thought local declarations did a new in the background and a dispose on return, using the heap.

Things only go on the heap if they are explicitly allocated, either directly or indirectly. If the object you create on the stack uses allocation with new, then that data would go on the heap. The pointer to that data would be on the stack. If the object you created was on the heap then both the pointer and the other data would both go on the heap. Its basically recursive. Say you created an object on the heap and called a member function. If that member function has local variables then they would be pushed onto the stack even though the object itself was allocated on the heap. This is due to the way the compiler treats executable code.

would object1 go on the stack when main was called? or is main a special case and it goes in the data segment?
main is exactly like any other function except it is called by the operating system rather than your program.

object1 (its member var storage space) would go in the data segment?
That would go in the data segment. While I said that main is a special case, it's slightly more complicated than I implied. main is actually called indirectly by the operating system in most environments. The operating system will generally call a function implemented in the run-time library. You can read a little about it here. That link mentions that the run-time library will initialize global variables before main is called. If the operating system called main directly then none of the constructors for the global variables would be called, among other things.

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!


#14 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 01:09 PM


A non-virtual member function and a stand-alone function are pretty much the same thing. The method address is known after linking.

 

ok, so the vmt addresses replace the non-virtual method calls at link time, correct?

 

i _thought_ they were the same thing....


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#15 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 01:13 PM


Things only go on the heap if they are explicitly allocated, either directly or indirectly.

 

i was under the impression that all were allocated on the heap, except global instances, which were allocated in the data segment at load time.

 

thanks for clarifying that. (lending a machete to my intellectual thicket).

 

and locally declared instances, being allocated on the heap, are automatically de-allocated when they go out of scope. and they don't use the heap so no possibility of memory leak. just the usual cross your i's and dot your t's stuff with malloc and free under different names (new and dispose) - correct?


Edited by Norman Barrows, 16 April 2014 - 01:18 PM.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#16 King Mir   Members   -  Reputation: 2050

Like
2Likes
Like

Posted 16 April 2014 - 01:14 PM

 

would object1 go on the stack when main was called? or is main a special case and it goes in the data segment?
main is exactly like any other function except it is called by the operating system rather than your program.

Almost. You cannot legally call main from your program in c++. This design was because the compiler may wish to insert additional setup code at the beginning of main.



#17 Servant of the Lord   Crossbones+   -  Reputation: 20974

Like
1Likes
Like

Posted 16 April 2014 - 01:23 PM

class MyClass; //Declare a class. Nothing created yet.

int main()
{
    MyClass *instance = new MyClass; //An instance created on the heap.

    MyClass instance; //An instance created on the stack.
}

If declared on the stack, the class is constructed when the code calls that function and the function reaches that line of code. Whether or not they are allocated before the function is called I *think* might be up to the compiler.

 

The above has to do with variables created inside functions.

 

If a variable is global, or if a variable is a static member-variable of a class (which is just a global variable in the class's namespace), then you can't count on the order or timing of when they are constructed. The only thing you can depend on is that they'll be constructed before int main() enters... but other than that, the order is compiler-specific.

 

This is one of the reasons why programmers avoid global variables in C++. I've dealt with, in my own code, far too many bugs caused by global variables.

 

Global variables are a convenience, but they end up hurting more than they help. Global constants are usually fine though.


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#18 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 16 April 2014 - 02:00 PM


If a variable is global, or if a variable is a static member-variable of a class (which is just a global variable in the class's namespace), then you can't count on the order or timing of when they are constructed. The only thing you can depend on is that they'll be constructed before int main() enters... but other than that, the order is compiler-specific.

 

right, startup code constructor hell. you can't rely on other globals being valid during your custom constructor. i hear about this a fair amount. i would think an explicit init() would take care of it. bad form?


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#19 frob   Moderators   -  Reputation: 22693

Like
0Likes
Like

Posted 16 April 2014 - 02:52 PM

If a variable is global, or if a variable is a static member-variable of a class (which is just a global variable in the class's namespace), then you can't count on the order or timing of when they are constructed. The only thing you can depend on is that they'll be constructed before int main() enters... but other than that, the order is compiler-specific.

 
right, startup code constructor hell. you can't rely on other globals being valid during your custom constructor. i hear about this a fair amount. i would think an explicit init() would take care of it. bad form?
Very bad.

I once had the misfortune of working on a port that severely abused global variables.

It took so long to run the pre-main initialization that we could not pass certification as-is. We had to hack in to the runtime and display the splash screen. Rather than the normal sleep while a splash was running, it spent those seconds initializing variables and doing other pre-main work.

IIRC it took something like five seconds between execution start and the beginning of main.

Just don't start down that road. It is a horrible thing.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I write about assorted stuff.


#20 TheComet   Crossbones+   -  Reputation: 1640

Like
0Likes
Like

Posted 16 April 2014 - 04:00 PM


except global instances, which were allocated in the data segment at load time.

 

It depends on the compiler you use, but generally yes, a global object will exist in the data segment, specifically in the bss section if it is a basic datatype or a structure containing basic datatypes. This isn't written in stone, though, and it's possible for it to also be allocated on the heap, or even on the stack.


Edited by TheComet, 16 April 2014 - 04:02 PM.

YOUR_OPINION >/dev/null




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS