Archived

This topic is now archived and is closed to further replies.

Class Declaration Oddities

This topic is 5304 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I noticed that I can declare a single class more than once, with different properties. Example: A Header has:
class cTest {
    int addtoa(int a);
}
  
which is called by the main function with a value (say.. 6). In a different source file I re-declare (and define) the class with:
class cTest {
    int addtoa(int a);
    int getadd();
}

int cTest::addtoa(int a)
{
     return a + this->getadd();
}

int cTest::getadd( void )
{
     return 4;
}
  
I notice that when calling from the main function, which only knows about the addtoa function, It still works and Adds 4 to the value (returning 10 when given 4). My Question: Is there any Negative effects to doing this? If not it is very useful because my external functions can use the class (and have the auto-syntax highlighting) while the class can hide it's workings (Up till now I've been using void pointers to devices so not everything needs to know what directx is - this, however is annoying). I can see that to keep classes together i could put the declaration all in one header, but use #ifdef 's to allow different syntax's. [edited by - Xgkkp on June 6, 2003 9:50:59 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
classes have a vtable which is a bunch of function parameters. thats really not a good way to do that... its way too confusing.

Share this post


Link to post
Share on other sites
only classes with virtual methods have a vtable

*EDIT* Ack... that's no good. You get stack corruption...

Would've been cool

[edited by - daerid on June 4, 2003 9:14:23 PM]

Share this post


Link to post
Share on other sites
errr... so does that mean that it doesn''t work? and why not, because it runs fine???

also, you can let something know a class exists by saying

class cTest;

and then accessing it''s members (even though autocomplete and stuff won''t work). Why is this method any different? I''m confused.

Share this post


Link to post
Share on other sites
The thing is, the header file let''s the source file know the size of the the class. The size is basically the sum of the size of all it''s aggregate members.

The source files will use the size that the definition in the header file gives. Where as the member functions defined in the actual .cpp file used to define the class methods will use a different definition, and thus a different size.

Think of it like this. The address of the object is also the address of it''s first data member. All subsequent data members can be thought of as just offsets from the address of the class.

So, let''s say you declare an instance of the class on the stack, local to a function. Now, that function uses the definition in the header file, which gives it a specific size. If all you do in that header file is define it''s public functions, then sizeof( class ) == 1. So (on most systems) a single byte will be allocated for that object. Now, let''s say you call a member function on that object.

The runtime now jumps to the address of that function, pushing as the first param of that function the this pointer of the class. The function that is being called uses the definition local to the source file that it was declared in, which contains in it the data members of that class. Since all a class is, is a blue-print on how to allocate memory for an object, and it''s different than the one used by the calling function, any member function that utilizes the member variables declared by the local definition of the class (inside the class''s source file), will be accessing data that the calling function has reserved for other uses, since it''s concept of the object is totally different than the member function''s. When the member function changes the data, you get stack corruption. In the case of a heap allocated object, you''ll probably get a "damage after normal block" error, or just a plain access violation error.

The explanation''s a bit complicated, but it shouldn''t be too hard if you think of a class definition as nothing more than a how-to on allocating and accessing memory. This is why all code that uses anything to do with a class''s definition MUST all use the same class definition, which is why they''re defined in header files that can be #included in source files.

Now, if you''re not accessing any member variables in the object, then you won''t have to worry about it. Then again, if that''s the case, you should just make the method static.

Share this post


Link to post
Share on other sites
Since the compiler compiles one source file at a time, it is not aware of (and doesn''t care about) duplicate types in other source files. The linker links the source files together and it uses the names that the compiler gives it. If you have something like the following, you are going to be very sorry:

x1.cpp
    struct Foo
{
... // x1 version of Foo
};
Foo foo1; // foo1 is global

x2.cpp
    struct Foo
{
... // x2 version of Foo which may be different from
// the x1 version
};
extern Foo foo1; // This is really the x1 version of Foo,
// but the compiler doesn''t know about the
// x1 version and assumes it is the x2
// version.


Putting the definition of Foo in a header file and including it in both x1.cpp and x2.cpp will prevent problems like this.

Share this post


Link to post
Share on other sites
Yeah, I know about the size things, but what if it was all declared in the same header file? and things like:

#ifdef DEFINE_FILE
IDirect3DDevice9 *D3DDevice;
#else
void *D3DDevice;
#endif

So It is still the same size, but my source file for the class can use pointers directly instead of having to type cast everything. This way, I can keep all the 3d stuff hidden without having to include all the directX stuff in every file.

[edited by - Xgkkp on June 5, 2003 12:19:44 PM]

Share this post


Link to post
Share on other sites
Whoa, hold up on the size issue.

class class1
{
public:
void add(int a);
int get();
private:
int i;
};

class class2
{
public:
void add(int a);
private:
int i;
};


sizeof(class1) = sizeof(class2), the number of functions doesn''t affect the size of the class. Only the amount of memory stored in it. The memory address of the functions within are not necessarily at an offset of the instantiation of the class. That''s why in OOP there is an extra pointer reference (and it''s why hardcore C proprieters claim that C is faster than C++). There is one memory reference to where class instance is stored, and and the standard memory reference to the functions which are the same for every instantiation.

And I don''t see how this affects the stack either. Aren''t all functions pushed on the stack in the same way? And since by default C++ inlines all member functions (unless "this->" is used since it''s a pointer to a function)?

And Shouldn''t any of these problems be resloved at link time anyway. Say you have one source file that uses one of the class declarations and your main uses the other. When they link to create the exe shouldn''t the compiler see that there are two classes of the same name and resolve any type differences that they might have? Simply it would be like masking the extra functions existence from the rest of the program.

Share this post


Link to post
Share on other sites
**WARNING** REALLY LONG POST

quote:
Original post by MindCode
Whoa, hold up on the size issue.

sizeof(class1) = sizeof(class2), the number of functions doesn't affect the size of the class. Only the amount of memory stored in it.



This is correct.

quote:
The memory address of the functions within are not necessarily at an offset of the instantiation of the class.



That's correct also, I never stated that they were.

quote:

That's why in OOP there is an extra pointer reference (and it's why hardcore C proprieters claim that C is faster than C++). There is one memory reference to where class instance is stored, and and the standard memory reference to the functions which are the same for every instantiation.



True. However, the object itself doesn't store the location of the methods. The compiler and linker resolve those at build time. Unless of course you're using classes that have virtual functions, then you're going through the vtable. Even then, you're just adding a pointer (usually just 4 bytes).

quote:

And I don't see how this affects the stack either.



It affects the stack if you instantiate a class of one size on the stack in one function (locally), and then call a member function on that object that is defined in another translation unit that expects a different size. If the member function expects more data ( probably because it's using a class definition that's different ), it'll over-write values higher up on the stack, and thus, cause corruption when the member function returns.

quote:

Aren't all functions pushed on the stack in the same way?



Yes, but they're just pushed onto the call-stack, and it's just a pointer to their location.


quote:
And since by default C++ inlines all member functions (unless "this->" is used since it's a pointer to a function)?



That's completely and utterly untrue. C++ will define as "inline" any member functions that are defined ( meaning the function body of code resides ) in the class definition itself.

Otherwise, it must be explicitly defined as inline, using the inline keyword, or it must be compiled in a source file, using the scope resolution operator( :: ).

Example:


class Test
{
public:
// This function is implicitly marked "inline"

// because it's body is enclosed by the class

// definition

int SomeFunc()
{
return 4;
}
// this function isn't _declared_ as inline

int AnotherFunc();
// This function is _NOT_ inline

int NotInlineFunc();
};

// This function wasn't declared as inline

// but it is _defined_ as inline, outside of

// the function body. This is basically equivalent

// to SomeFunc() above

inline int Test::AnotherFunc()
{
return 10;
}

///////////////////////////////////////////////////

// Test.cpp


int Test::NotInlineFunc()
{
return 1000;
}
///////////////////////////////////////////////////



quote:

And Shouldn't any of these problems be resloved at link time anyway. Say you have one source file that uses one of the class declarations and your main uses the other. When they link to create the exe shouldn't the compiler see that there are two classes of the same name and resolve any type differences that they might have?



No. That defeats the whole purpose of being able to compile separate source files. There's a difference between "compile time" and "link time". They are two completely separate phases.

During compile time, the actual binary machine code is generated. When the compiler compiles a source code file, it's ONLY concern is that source code file. It has NO KNOWLEDGE of anything that you don't explicity tell it. That's why you #include header files. So that you can give the compiler the information it needs to generate correct code.

Now, at link time, all you're doing is taking all those separate object files that the compiler produced, putting them together into one big image along with any other libraries needed, and then you're resolving all the function calls.

Since it all ends up in one big image anyways, you need to make sure that all your class definitions are consistent, and that anything that uses a class definition (meaning calls a member function, or allocates an instance of that class on the stack), ABSOLUTELY MUST ALL use the same definition.
Otherwise, you start confusing the compiler and linker and getting memory corruption and/or stack corruption.

quote:

Simply it would be like masking the extra functions existence from the rest of the program.



The problem isn't the member functions. The linker takes care of that. The problem is at compile time, with the data members and the size of the object.

For example, you define SomeClass in the header file SomeClass.h to have a single int data member. So, sizeof( SomeClass ) will yield 4 (on most compilers), and any code that includes SomeClass.h that tries to allocate an object of type SomeClass, will only allocate 4 bytes.

Now, in the source file SomeClass.cpp, you opt to not include the header file, and just re-define the class in that file.

This time however, you add another int member name "namelen", and a char* member named "name". Now, in that source file, all the member functions are expecting sizeof( *this ) to be 12 ( 4 bytes for the int, another 4 for the char* pointer ).

So you go happily about your business, and in main.cpp, or somewhere else, you do this:

SomeClass* p = new SomeClass();
This source file just #includes SomeClass.h. So, when operator ::new( size_t size ) is called, it's called with size == sizeof( SomeClass ), which is 4 (because of the definition in the header file).

Now you go and call a method on that pointer, let's say SomeClass::SetName( const char* newname ), which looks like this:

void SomeClass::SetName( const char* newname )
{
if( this->name )
delete[] this->name;
// oops! this variable doesn't have any space allocated for it

// because this variable is located at offset 4 thru 8,

// which doesn't exist, because the calling code

// only knew about that single int, which is at

// offset 0 - 4

this->namelen = strlen( newname );
// same goes for this->name, which _should_ be at

// offset 8 - 12, but those locations don't exist,

// because the calling function only allocated 4 bytes

this->name = new char[ this->namelen + 1 ];
strcpy( this->name, newname );
}


So *POW* you get memory corruption. This instance it's on the heap. However, the same goes for stack-allocated objects. It doesn't matter *where* the memory is, it'll still get corrupted.

I hope that all made sense...

[edited by - daerid on June 5, 2003 2:31:13 PM]

Share this post


Link to post
Share on other sites
Do this


class ClassIDontWantToIncludeInMostFiles;
class AnotherOne;

class MyThing {
private:
ClassIDontWantToIncludeInMostFiles myPriv1;
AnotherOne myPriv2;
public:
...
}


1. You don''t need to include ''AnotherOne'' stuff in other .cpp files
2. ''private'' is for the stuff you don''t want other .cpp files to care about.
3. You use this same header for any .cpp files that uses MyFile, even the one that implements it

Share this post


Link to post
Share on other sites
quote:
Original post by C-Junkie
Do this...


That won''t work as is. You have to put a pointer or a reference, to the forward declaration of the class.

Share this post


Link to post
Share on other sites
that''s true.

its also the only method that works with what he wants... Its trivial to manage allocating and freeing the memory in the constructor and deconstructor.

Share this post


Link to post
Share on other sites
So....
Is there nothing wrong with this:
#ifdef DEFINE_FILE
IDirect3DDevice9 *D3DDevice;
#else
void *D3DDevice;
#endif

And will this work?


class Foo;

class MyThing {
private:
Foo *myPriv1;
public:
...
}

If I actually have the header with class Foo defined included in just the classes source file?

Share this post


Link to post
Share on other sites
quote:

class cTest
{
int addtoa(int a);
}


dont you mean:

class CTest
{
public:
int addtoa(int a);
}

anyways,
quote:

class Foo;
class MyThing {private: Foo *myPriv1;public:...}

If I actually have the header with class Foo defined included in just the classes source file?


yes, this will work, as long as myPriv1 is a allocated at run-time and not compile-time.



doh, nuts. Mmmm... donuts
My website

[edited by - brassfish89 on June 6, 2003 12:56:46 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by Xgkkp
So....
Is there nothing wrong with this:
#ifdef DEFINE_FILE
IDirect3DDevice9 *D3DDevice;
#else
void *D3DDevice;
#endif

And will this work?


class Foo;

class MyThing {
private:
Foo *myPriv1;
public:
...
}

If I actually have the header with class Foo defined included in just the classes source file?



Yes, that will work. As long as you just store a reference or a pointer to the forward declared class, it''ll be just fine.

In fact, this is the status-quo on how to solve your problem

Share this post


Link to post
Share on other sites
quote:
Original post by daerid
In fact, this is the status-quo on how to solve your problem


Hurray! Now I can use it without feeling that I''m soing something inherently wrong..

Share this post


Link to post
Share on other sites