A few C++ basics
james_lohr

I thought I'd start using C++ for the first time mainly because I wanted Java-style multi-threading (Boost's C++ thread library is great!).

Anyway, I've not got the time to read up on C++ (exams approaching), so I thought I'd just jump straight in and deal with the problems as they occur.

First question:

Is the following a memory leak?

MyObject newObj; //ie what does this line do exactly?
newObj = MyObject(arg1,arg2);

Is this prefered? :

MyObject *newObj;
newObj = new MyObject(arg1,arg2);

Kitty Cat
Quote:

Is the following a memory leak?

MyObject newObj; //ie what does this line do exactly?
newObj = MyObject(arg1,arg2);

Not necessarilly. It allocates an object on the stack, and runs it's default contructor (first line), then it allocates another object and copies it over the original. as long as the object itself takes care not to leak memory, there's no problem.

I'd say the latter is preferred, though. You generally don't allocate objects on the stack unless they're small with specific uses.

james_lohr
Quote:

Not necessarilly. It allocates an object on the stack, and runs it's default contructor (first line), then it allocates another object and copies it over the original. as long as the object itself takes care not to leak memory, there's no problem.

I see, so MyObject(arg1,arg2) also creates an object on the stack which is discarded after the copy? Can't I declare an object without creating anything (without using the pointer approach) ?

Kitty Cat
Quote:

Can't I declare an object without creating anything (without using the pointer approach) ?

extern
If you're meaning you want to use the non-default constructor right off the bat, you can do this:
MyObject myobj(arg1, arg2);

james_lohr

Ok great. So even if there is no zero-parameter constructor specified in the class, MyObject myobj; will still construct an object regardless - as if a MyObject(){} was present in the class?

BAF

Can you make the default constructor with 0-parameters private to disallow "MyObject myobj;" ?

CGamesPlay

You can.

james_lohr

Ok, so if objects exist on the stack, then (unlike Java) the following won't work?

MyObject *a;

{
  MyObject newObj(1,2,3);  
  a = &newObj;
}

a->doSomething();

CGamesPlay

That will crash, yes.

gillius

That might work fine if doSomething was inlined so that the stack is not corrupted... But you have a dangling pointer here so the behavior is undefined even if it doesn't crash.

For basic stuff, C++ isn't magical. You've probably done the same in C and Allegro is object-oriented as well. In C, defining a struct and then declaring a struct object actually builds it (and this is identical in C++ when using default constructor). The following C++ and C code are equivalent in the generated binary code (ignoring a pedantic detail):

MyObject a(1, 2, 3);
a.doSomething();

//C code
MyObject a;
create_a( &a, 1, 2, 3 );
doSomething( &a );

Basically basic object stuff in C++ you can just basically assume it's like a C function with a hidden "this" parameter. There's a little more to it than that, which would rile up a lot of C++ experts here, but that's basically it.

james_lohr

Someone mind explaining defaults? - this snippet is taken from the OpenLayer demo:

   Particle( float x, float y,
             ColorScheme scheme,
             float minSpeed = particleSpeedMin,
             float maxSpeed = particleSpeedMax,
             float startAngle = particleStartAngle,
             float angleSpread = particleMaxAngleSpread,
             float gravity = particleGravity )
         : gravity( gravity ), circle( Vec2D( x, y ), 1.0 ) {

.
.
.
}

It is a constructor. What exactly does the stuff following ":" mean? Also, what is the signature for this function if I want to make a header file?

Trezker

That's initializing.
Instead of doing member=value in the function body you do it like that.
This is supposedly faster, but I don't know the specifics.

james_lohr

Thanks. Another topic completely: what's the deal with enums and header files? - Do I need to make a global header with all enums?

CGamesPlay
Quote:

Do I need to make a global header with all enums?

Not at all. You can put enums wherever you can put classes.

Trezker

Never ever make a global header, if you have a global header you've done something wrong.
Only include the least amount of headers neccesary to compile.

CGamesPlay
Quote:

Only include the least amount of headers neccesary to compile.

So you mean like one global header? :P

Kitty Cat
Quote:

What exactly does the stuff following ":" mean?

It's an initialization list and it allows you to initalize the members of the class. Basic types like int or char* can take something of equal type and will be set to it. But for something like a class it allows you to use a non-default contructor. Like so:

class Foo {
   Foo(int a);
}

class Bar {
   Foo foo;

   Bar(int a)
   : foo(a)
   {
      /* foo will have initialized using the non-default constructor */
   }
};

Note that compilers will complain if you initialize class members out of order.

james_lohr

Ok, next thing: circular dependencies. Seems pretty fundamental, I'm not sure why I've never encountered this problem in plain C, but anyway: Suppose class A uses class B, class B uses C and C uses A.

file A.hpp:

#include "B.hpp"
...

file B.hpp:

#include "C.hpp"
...

file C.hpp:

#include "A.hpp"
...

How do you usually solve this issue? - If I try to use forward declarations, I get "invalid use of undefined type".

CGamesPlay

Don't use the type. You need to forward declare the types, and you need to put the method bodies inside the source files.

james_lohr
Quote:

Don't use the type.

What exactly do you mean?

Kitty Cat
// a.hpp:
class B;

class A {
   B *foo;
   void somefunc();
};

// a.cpp:
void A::somefunc()
{
   foo->blah();
}

james_lohr

That doesn't work. As I said, it gives an "invalid use of undefined type" at foo->blah();

CGamesPlay

You forgot to #include b.h in a.cpp.

james_lohr

That's what I was doing originally, but it gave some bizzare linker errors. Tried it again, got the linker errors again.. but after a second rebuild of everything they went away. So it's working now at least.

Thanks :)

ImLeftFooted
Quote:

It is a constructor. What exactly does the stuff following ":" mean?

After : is a comma delimited list of constructor calls for all inherited classes and attributes. Its also valid C++ to put in try and catch blocks.

Quote:

Also, what is the signature for this function if I want to make a header file?

Well this function probably already exists in a header, as default parameters can only be specified in a function definition (this is Java style). If you were to separate the function into a header definition and implement the function elsewhere in some cpp file the function would probably look like this:

   Particle( float x, float y,
             ColorScheme scheme,
             float minSpeed = particleSpeedMin,
             float maxSpeed = particleSpeedMax,
             float startAngle = particleStartAngle,
             float angleSpread = particleMaxAngleSpread,
             float gravity = particleGravity );

Note that the : syntax is only valid in the actual function implementation (ie in the .cpp file). Also note the semicolon.

james_lohr

Thanks.

Now onto inheritance:

Suppose X,Y are subclasses of A, and B has a pointer to a class of type A (which may be instanciated with a subclass X or Y):

1 
2class A{
3 public:
4 int a,b,c;
5};
6 
7class X:public A{
8 public:
9 int x,y,z;
10};
11 
12class Y:public A{
13 public:
14 int x,y,z;
15};
16 
17class B{
18 
19 public:
20 C(A *_a){
21 a = _a;
22 }
23 
24 int sizeOfIt(){
25 return sizeof(*a);
26 }
27 
28 A *a;
29};
30 
31/////////////////
32 
33B b(new X());
34 
35return b.sizeOfIt();

In the above, b.sizeOfIt() will return the size of the parent class instead of the actual child class. How would you usually deal with this? How do you know which subclass *a is pointing to?

[edit] Should I rather use generic class / templates for this?

ImLeftFooted
Quote:

How do you know which subclass *a is pointing to?

The correct is answer is you should not know (or care). That said it is still possible to find out.

Heres a more 'proper' soultion:

1class A{
2 public:
3 int a,b,c;
4 virtual int sizeOfMe() { return sizeof(*this); }
5};
6 
7class X:public A{
8 public:
9 int x,y,z;
10 virtual int sizeOfMe() { return sizeof(*this); }
11};
12 
13class Y:public A{
14 public:
15 int x,y,z;
16 virtual int sizeOfMe() { return sizeof(*this); }
17};
18 
19class B{
20 
21 public:
22 C(A *_a){
23 a = _a;
24 }
25 
26 int sizeOfIt(){
27 return a->sizeOfMe();
28 }
29 
30 A *a;
31};
32 
33/////////////////
34 
35B b(new X());
36 
37return b.sizeOfIt();

Given these changes you should be specifying virtual desctructors as well, otherwise calling 'delete' will have undefined results.

Edit
You have a perfect case here to use : intializer lists.

        C(A *_a){
            a = _a;
        }

Could be rewritten

        C(A *_a) : a(a_) {
            
        }

X-G

That's right, generally the point is that you shouldn't have to know. If you really want to find out the type, GNU C++ does have a typeof operator.

Kibiz0r

You should never have to use a parent class to access child-only functionality. This is why virtual functions exist!

That said, dynamic_cast will cast further along the inheritance chain, but it's something that should be avoided unless it absolutely cannot be helped, and I can think of very few instances where it can't.

james_lohr

Yeah, I realised that I was inventing an exceptional case when in fact overriding was the way to go.

Quote:

you should be specifying virtual desctructors as well, otherwise calling 'delete' will have undefined results.

Great thanks! That one slipped me by: I'll need to fix that right away.

Tobias Dammers

Rule: If a class has any virtual member functions, the destructor must be virtual, too.

Quote:

Well this function probably already exists in a header, as default parameters can only be specified in a function definition (this is Java style). If you were to separate the function into a header definition and implement the function elsewhere in some cpp file the function would probably look like this:

You mean 'declaration', not 'definition', right? The definition says what it is, the declaration says that is exists and what it looks like.

Quote:

The correct is answer is you should not know (or care). That said it is still possible to find out.

While this is generally true, there are some situations where, for whatever reasons, you need a quick way to find out the data type of a polymorphic class instance. One example may be collision response in a game: depending on what kind of object something collides WITH, the response may be different. A very fast yet easy way is to have a virtual cMyClass::get_type() const function that returns a different type ID (say from an enum) for each class. It's often faster than RTTI. The border isn't really that clear BTW: If, for example, your class can save itself to a chunk file, it probably comes with a get_chunk_id() function anyway, which can easily double as type information.

Thread #591465. Printed from Allegro.cc