Jump to content


C++ OO problem, virtual function & abstract class


15 replies to this topic

#1 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 17 August 2007 - 08:30 AM

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;


}



#2 bramz

    Valued Member

  • Members
  • PipPipPip
  • 189 posts

Posted 17 August 2007 - 11:49 AM

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.
hi, i'm a signature viruz, plz set me as your signature and help me spread :)
Bramz' warehouse | LiAR isn't a raytracer

#3 Nils Pipenbrinck

    Senior Member

  • Members
  • PipPipPipPip
  • 597 posts

Posted 17 August 2007 - 11:52 AM

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
My music: http://myspace.com/planetarchh <-- my music

My stuff: torus.untergrund.net <-- some diy electronic stuff and more.

#4 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 17 August 2007 - 03:27 PM

thanks ALOT...

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

thanks ALOT again to bramz & Nils Pipenbrinck ...

#5 Nils Pipenbrinck

    Senior Member

  • Members
  • PipPipPipPip
  • 597 posts

Posted 17 August 2007 - 06:29 PM

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)
My music: http://myspace.com/planetarchh <-- my music

My stuff: torus.untergrund.net <-- some diy electronic stuff and more.

#6 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 18 August 2007 - 03:03 PM

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 !

#7 Jare

    Valued Member

  • Members
  • PipPipPip
  • 247 posts

Posted 18 August 2007 - 03:53 PM

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;

}



#8 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 18 August 2007 - 09:07 PM

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 !...

#9 martinsm

    Member

  • Members
  • PipPip
  • 88 posts

Posted 18 August 2007 - 10:04 PM

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

private:
    pointer* A;
};


#10 Reedbeta

    DevMaster Staff

  • Administrators
  • 5308 posts
  • LocationSanta Clara, CA

Posted 19 August 2007 - 02:01 AM

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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#11 ldrrn

    New Member

  • Members
  • PipPip
  • 29 posts

Posted 19 August 2007 - 11:48 PM

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();

 }

};



#12 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 21 August 2007 - 07:04 AM

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 !

#13 Reedbeta

    DevMaster Staff

  • Administrators
  • 5308 posts
  • LocationSanta Clara, CA

Posted 21 August 2007 - 07:14 AM

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 ;)
reedbeta.com - developer blog, OpenGL demos, and other projects

#14 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 21 August 2007 - 07:52 AM

Quote

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 !

#15 Akinak

    Member

  • Members
  • PipPip
  • 52 posts

Posted 26 August 2007 - 06:28 AM

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 ??

#16 Reedbeta

    DevMaster Staff

  • Administrators
  • 5308 posts
  • LocationSanta Clara, CA

Posted 26 August 2007 - 02:39 PM

Vector3D * v = new Vector3D[3];
and don't forget to delete[] it when you're done.
reedbeta.com - developer blog, OpenGL demos, and other projects





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users