C++ OO problem, virtual function & abstract class

141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 17, 2007 at 08:30

hi everybody again…

i want to baild an abstract class called: Shape with virtual function “draw”…
a ShapeGroup class will store shapes and draw them !
in ShapeGroup class “DrawAllShapesfunction” we dont know witch shape we are drawing so i use the base class Shape !
but i got a linker error :

unresolved external symbol "public: virtual void __thiscall Shape::draw(void)" 

i dont want to set Draw function for base class “Shape”, i want to call its deriven classes; witch we dont know their number, name or …

#include <stdio.h>

class Shape {
public:

    virtual void draw();

    /*
    virtual void draw() {
        printf(" Main Shape Draw Function \n");
    }
    */
};

class Rectangle: public Shape {
public:
    virtual void draw() {
        printf("Drawing Shape is Rectangle !\n");
    }
};

class Circle : public Shape {
public:
    virtual void draw() {
        printf("Drawing Shape is Circle !");
    }
};

class ShapeGroup {
public:
    ShapeGroup() {
        shapeCounts = 0;
        shapes = new Shape[10];
    }

    void AddShape(Shape sh) {
        shapes[ shapeCounts++ ] = sh;
    }

    void DrawAllShapes() {
        for (int i = 0; i < shapeCounts; i++) {
            shapes[i].draw();
        }
    }

private:
    int shapeCounts;
    Shape *shapes;
};

int main() {
    ShapeGroup *group = new ShapeGroup();

    Circle c;
    Rectangle r;
    
    group->AddShape(c);
    group->AddShape(r);

    group->DrawAllShapes();

    while (true) {} // so we can see output window...

    delete group;

    return 0;

}

15 Replies

Please log in or register to post a reply.

25bbd22b0b17f557748f601922880554
0
bramz 101 Aug 17, 2007 at 11:49

OK, where to start?

I’m gonna link to bits of information for further explanation. That makes my job easier =)

(1) If you’re going to use a base class like this, you should have a virtual destructor too.

(2) In an abstract base class, you usually want to declare you virtual functions pure virtual, by adding the “= 0”.

(3) You usually want classes in an inheritance hierarchy to be non-copyable and to use a cloning mechanism instead, to avoid the chance on slicing. Slicing is what happens if you have a reference to a Shape that is actually a Rectangle and you make a copy. Then the copy will only contain the Shape part and not the Rectangle. The link will explain it better, the only reason I mention it here is because it’s important in (4)

(4) When dealing with arrays of objects from a inheritance hierarchy, you don’t want to store an array of the base class, but an array of POINTERS TO the base class. Otherwise, when you push a Rectangle in the collection, you will SLICE the object to a shape and you will loose the Rectangle information. Actually, once you have a pure virtual function, you will find it impossible to have an array of Shapes. The only thing you’ll have to take care of, is to delete the objects explicitly on destruction of the ShapeGroup.

(4 bis): it’s best to use the heap to allocate objects in an inheritance hierarchy

(5) an extra: it’s easier to use a std::vector instead of plain arrays.

(6) another extra: for abstract base classes, it’s good practice to declare the constructor as protected.

#include <stdio.h>
#include <vector> // (6)

class Shape {
public:
    virtual ~Shape() {} // (1)
    virtual void draw() = 0; // (2)
protected:
    Shape() {} // (6)
};

// Rectangle and Circle are OK

class ShapeGroup {
public:
    ShapeGroup() {
        // nothing to be done thanks to (5)
    }
    ~ShapeGroup() {
        // vectors can be accessed like arrays, 
        // and they know their own size (5)
        for (int i = 0; i < shapes.size(); i++) {
            delete shapes[i]; // delete shapes explicitly (4)
        }
    }

    void AddShape(Shape* sh) { // pointer to shape, due to (4)
        shapes.push_back(sh); // vector syntax, (5)
    }

    void DrawAllShapes() {
        for (int i = 0; i < shapes.size(); i++) { 
            // we're storing pointers (4), so -> instead of .
            shapes[i]->draw(); 
        }
    }

private:
    std::vector<Shape*> shapes; // use vector (5) of pointers (4)
};

int main() {
    ShapeGroup *group = new ShapeGroup();

    Circle* c = new Circle; // (4 bis);
    Rectangle* r = new Rectangle; // (4 bis);
    
    group->AddShape(c);
    group->AddShape(r);

    group->DrawAllShapes();

    while (true) {} // so we can see output window...

    delete group;

    return 0;

}

There are still some issues left, like using auto_ptr in main() to protect against possible memory leaks, but that’s for later. Also, in this example it is not necessary to allocate ShapeGroup on the heap. And of course I assume that the while (true) {} loop is just a oversimplification of the real situation.

B91eae75cd6245bd8074bd0c3f1cc495
0
Nils_Pipenbrinck 101 Aug 17, 2007 at 11:52

Hi Akinak,

You’ve forgotten to declare the shape::draw as abstract. For this reason the linker complains that the implementation of shape::draw is missing.

This here should do the trick:

class Shape {
public:

    virtual void draw() = 0;
};

Nils

141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 17, 2007 at 15:27

thanks ALOT…

now i just need to gather some information on <vector> ! …

thanks ALOT again to bramz & Nils Pipenbrinck …

B91eae75cd6245bd8074bd0c3f1cc495
0
Nils_Pipenbrinck 101 Aug 17, 2007 at 18:29

No problem mate…

Btw, I kinda disagree with bramz that you should use vectors. For a full time C++ programmer that would be the way to do it, but I assume you’re learning the c++ language, and using the STL (vectors) may do more harm than good. You can use them “as is” without knowing what goes on inside of them, but otoh you should first brush up your skills of what the c++ language can do.

Keep things simple at first and ignore the STL for a while..

Btw - the only reason why I advice against using STL at the moment is, that you can’t simply single-step through your programs anymore without entering the horrid world of template meta programming headers (STL)

141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 18, 2007 at 15:03

i want to ask this question here too !

i have a namespace “TESTING” and a global integer in it “member” , “TESTING” is defined in a header file.
two .cpp files include “TESTING” header…
but a linker error is result :

error LNK2005: "int Testing::member" (?member@Testing@@3HA) already defined in f1.obj

Testing Header file “testHeader.h”

#ifndef TESTHEADER_H
#define TESTHEADER_H

namespace Testing {
    int member = 100;
}
#endif

file #1 that include “testHeader.h” ,, “f1.cpp”

#include "testHeader.h"

void function1() {
 int value1 = Testing::member;
}

file #2 that include “testHeader.h” ,, “f2.cpp”

#include "testHeader.h"

void function2() {
  // or change it !
 Testing::member = 150;
}

int main() {
    return 0;
}

is this possible to define a variable in namespace and include it in 2or3 cpp files and access it ?
this variable could be a class or struct too !

6b7e1a4b42e4b47d92fdef8bf2bd8e2c
0
Jare 101 Aug 18, 2007 at 15:53

Aside from the horror of function2() changing a global, all you need to do is to move the initialization of Testing::member into a cpp file.

Testing.h:

#ifndef TESTHEADER_H
#define TESTHEADER_H
namespace Testing {
  extern int member;
}
#endif

Testing.cpp:

#include "Testing.h"

namespace Testing {
  int member = 100;
}
141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 18, 2007 at 21:07

thanks “Jare”, i got it..
now a new question !

can i limit access to that global variable… !
for example , i want a pointer to a struct. this pointer should share in namespace but , classes out of namespace couldnt access it…

for example again :

/// file 1
namespace TEST {
   pointer *A;
   class TEST_CLASS;
}

//// file 2
#include <TEST>

TEST::TEST_CLASS {
     //access to A is available becouse TEST_CLASS is a member of TEST !
     A->DoSmthing();
};

//// file 3

#include <TEST>

int main() {
  // access to TEST::A is not available !
   TEST::A->DoSmthin(); // Error !
   TEST::TEST_CLASS; // access OK !
}

is this possible ? how !?

thanks agian to anybody who helps me !
and sorry for my poor English !…

8fd4a055522ce713cde7dd1cb4083cb2
0
martinsm 101 Aug 18, 2007 at 22:04

I think you should use variable A as private member in class TEST_CLASS:

class TEST_CLASS
{
    // ...

private:
    pointer* A;
};
A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 19, 2007 at 02:01

It would have to be a static member to get the same behavior as the sample he wrote.

But yes, the only way in C++ to restrict access to something is make it a private/protected member of a class. Namespaces do not have any similiar thing.

4b48cbb89dff3ccf544ff85466967ab9
0
ldrrn 101 Aug 19, 2007 at 23:48

besides making the variable to a static private member of TEST_CLASS, you should make a TEST class as friend class of TEST_CLASS. by doing this only TEST class can access private member of TEST_CLASS

class TEST_CLASS
{
...
private:
 static pointer* A;
public:
 friend class TEST;
};

class TEST
{
...
 void doSomething()
 {
   TEST_CLASS::A->doSomething();
 }
};
141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 21, 2007 at 07:04

your answer was great ! but i got a linker error that my “pointer *A” isnt valid!

1>RenderFactory.obj : error LNK2019: unresolved external symbol "private: static struct IDirect3DDevice9 * AGE::CORE::RenderFactory::device" (?device@RenderFactory@CORE@AGE@@0PAUIDirect3DDevice9@@A) referenced in function "public: void __thiscall AGE::CORE::RenderFactory::RedrawBuffer(void)" (?RedrawBuffer@RenderFactory@CORE@AGE@@QAEXXZ)

i place this code in a cpp file but it wont solve the problem !

A = 0;

and 1 new that i found during this was: C++ compiler will suppose to a class is friend of itself !

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 21, 2007 at 07:14

Static variables in a class need to be defined, not just declared. The declaration in the class just tells the compiler that the variable exists, but the definition tells it to actually allocate space for it and give it an address.

To define the variable, put this in a cpp file somewhere (NOT in the header file):

IDirect3DDevice9 * AGE::CORE::RenderFactory::device = 0;

As for classes being friends of themselves, yes…they are able to access their own private members, wouldn’t be much use if they couldn’t ;)

141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 21, 2007 at 07:52

wouldn’t be much use if they couldn’t

well , im moveing from JAVA to C++ , and java dont do such thing , now i think C++ is a REAL GREAT BIG WHITE WORLD !

141b3a7759df12dd7c467893d4a8701e
0
Akinak 101 Aug 26, 2007 at 06:28

well
i have a new question and i want to ask it here again ::

how i can build a free space in memory for an array of structs ?

Vector3D *v;
::ZeroMemory( &v , sizeof(Vector3D) * 3);
v[0] = Vector3D(10 , 14 , 12);
v[1] = Vector3D(12 , 14 , 15);
.....

there is no compiler error, but this error is result when i run my app:

Unhandled exception at 0x00401cea in AGE.exe: 0xC0000005: Access violation writing location 0x00000000.

how i can build such array ??

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Aug 26, 2007 at 14:39
Vector3D * v = new Vector3D[3];

and don’t forget to delete[] it when you’re done.