Jump to content

Functions and Polymorphism


AggrorJorn
 Share

Recommended Posts

I have a list of GUI elements. GUI elements like button, checkbox etc inherit from a base GUI class.

 

post-45-0-96734800-1338315946.jpg

 

The list is of the base Type:

vector<LCPGui*> mainMenuElements;

The elements added are checkboxes and buttons.

 

Via a for loop I want to call their respective draw functions:

for(int i = 0; i < mainMenuElements.size(); i++)
mainMenuElements.at(i)->Draw();

 

The Draw function that is being called, resides in the GUI class, whiles I need the Draw function from the sub classes. 1 way to solve this would be to add the 'virtual' keyword to the Draw function in the base class. However sometimes I need to use the base class Draw function.

 

How can I call the Draw function from the subclasses?

Link to comment
Share on other sites

GUI::Draw();

When this is used inside a child class, it doesn't need to be a static method, but it will actually call the instanced base class' method (which is kinda not instanced alone, but fusioned into one instanced with the child class).

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Doesn't matter if it's at the beginning or somewhere else in the method, but yeah, usually it's in the beginning because of practical reasons, that the coder wants the base class done first and then only add his own stuff to it.

 

Your question is a bit wierd, so I'm not sure if you understood my first answer. What I meant is that you have a child class method like this:

class GUI
{
   public:
   void Draw()
   {
       DrawBox();
   }
};

class MyChild : public GUI
{
public:
void Draw()
{
	GUI::Draw();
	DrawCircle();
}
};

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

class GUI
{
public:
  virtual void Draw()  // important ... virtual must be here
  {
	    // do your base things
 }
};

class CheckBox : pubic GUI
{
public:
   void Draw()
   {
		  GUI::Draw();  // base class GUI::Draw is executed
		  // Do you specialized drawing here
  }
};
///////// using /////
void  example()
{
   std::vector<GUI*> guis ;
   guis.push_back( new CheckBox() );
   guis.push_back( new CheckBox() );
   guis.push_back( new CheckBox() );
   // draw them
  for( std::vector<GUI*>::iterator it = guis.begin(); it != guis.end(); it++ )
		    (*it)->Draw()
   // another way to draw them
   std::for_each( guis.begin(), guis.end(), [ ](GUI* pGui)
   {
	  pGui->Draw();
   });
  // ....
  // ....
  // ....
  // ....




AV MX Linux

Link to comment
Share on other sites

You are wrong Roland.

Virtual is never needed in C++, and it only creates bloated and slow code.

I just tried this example and it works fine, it prints:

Cow

Horse

 


#include <cstdio>

class GUI
{
public:
void Draw()
{
printf("Cown");
}
};

class Child : public GUI
{
public:
void Draw()
{
GUI::Draw();
printf("Horsen");
}
};

int main()
{
Child tia;
tia.Draw();
}

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Thanks for the answers guys.

 

I haven't explained it good enough: When I use the draw function in the loop, I purely want to call the Draw function of that object type. So the draw function of the button/checkbox. I Don't want to use the Draw function from the base class. Perhaps this is not possible. If so, then I need to rearange some code and make the base class Draw function empty and add virtual to it.

 

@Metatron, Yes this works, but here you define tia as a Child and not as a Horse. The list exists out of base elements but the actual items are sub elements.

Link to comment
Share on other sites

You are wrong Roland.

Virtual is never needed in C++, and it only creates bloated and slow code.

Of course I'm not wrong. You are only showing that you don't understand the idea with virtual

Listen to Aggror :) He's got it.

AV MX Linux

Link to comment
Share on other sites

Thanks for the answers guys.

 

I haven't explained it good enough: When I use the draw function in the loop, I purely want to call the Draw function of that object type. So the draw function of the button/checkbox. I Don't want to use the Draw function from the base class. Perhaps this is not possible. If so, then I need to rearange some code and make the base class Draw function empty and add virtual to it.

 

@Metatron, Yes this works, but here you define tia as a Child and not as a Horse. The list exists out of base elements but the actual items are sub elements.

 

OK. The change is simple enough to make that happen

class GUI
{
public:
	  virtual void Draw() = 0; // important ... virtual must be here
	  // = 0 means that its abstract with no code
};
class CheckBox : pubic GUI
{
public:
	   void Draw()
	   {
					  // Do you specialized drawing here
	  }
};
///////// using /////
void  example()
{
	   std::vector<GUI*> guis ;
	   guis.push_back( new CheckBox() );
	   guis.push_back( new CheckBox() );
	   guis.push_back( new CheckBox() );
	   // draw them
	  for( std::vector<GUI*>::iterator it = guis.begin(); it != guis.end(); it++ )
					    (*it)->Draw()
   // another way to draw them
   std::for_each( guis.begin(), guis.end(), [ ](GUI* pGui)
   {
			  pGui->Draw();
   });
  // ....
  // ....
  // ....
  // ....


AV MX Linux

Link to comment
Share on other sites

You can make seperate lists for each class, that works fine, and makes faster code. Then you have can have one method which loops through all the class lists. Also for memory allocation you should never use "new" or "malloc"; but always "alloca", because it uses fast linear stack memory and not slow fragmented heap memory.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Assembler is not cross-platform, and with normal coding, it's also slower than C/C++, because the compilers do insane optimizations which nobody can do by hand in assembler. Speed was an issue earlier, then there were times when PCs were fast (when Doom3 came out, and then again when Crysis 1 came out), and now they are slow again, because everyone buys the cheapest laptop they can get, so speed IS an issue. It's the game industry which dictates what PCs are sold. If nobody makes AAA games, nobody produces AAA PCs; and not the other way around.

 

Here's a realistic example. I am normal customer who wants to play BF3, so I go to a computer shop and ask for a PC which can run this game well. I also want to play with my friends in the new AAA MMO, so the computer should run that too. Then I get an AAA PC, and the number of low end PCs goes down by 1 on the global market.

 

I remember when we had like 50 people in our WoW guild, and they had the highest spec AAA PCs because playing WoW was very important to them, because all IRL friends played it too (and nobody had time for facebook because of the raiding and forum tactics topics), and they didn't want to lose some hard earned epix because of slow graphics or sluggish computer.

 

Today all IRL friends are on facebook, and play facebook games, so nobody buys AAA PCs anymore.

 

So facebook killed the AAA PCs, and PC gaming. Now facebook even wants to kill AAA phones too, by making their own iPhone clone. They already hired a bunch of Apple employees, which I heard on rock radio today.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

You can't declare virtual methods in the cpp file, they work only in headers. It's like somekind of religion, totally fake.

So you must put all the code into the header, or go back to my gnostic method of programming fast programs.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

GUI.H

============

#pragma once

class GUI
{
    virtual void Draw( ) = 0;
};

 

 

CHECKBOX.H

============

#pragma once
#include "GUI.H"

class CheckBox: public GUI
{
public:
      void Draw();
};

 

CHECKBOX.CPP

===================

#include "CheckBox.h"

void CheckBox::Draw()
{
     // Draw here
}

AV MX Linux

Link to comment
Share on other sites

Here is a link, there are some tests about how "slow" virtual functions really are.

http://stackoverflow.com/questions/449827/virtual-functions-and-performance-c

 

On modern CPUs its so fast that you will "feel" a difference at round about 1 million calls.

 

You can create a whole engine with virtual functions (which is good to hide implementation specific details but i won't write all with virtual functions for example vector3 or matrix etc.) and its run without problems, because to load/draw a model is much (very much) slower then calling virtual functions.

Link to comment
Share on other sites

Every bit counts. And 1 million iterations is quite low because I usually make speed tests with 10 billion iterations to get a worthy difference.

Anyway, I'm not concerned about little speed differences at all, especially if they don't matter to the user at all.

It's more a question of the path of decision. When I test something, then I use the results to show the right path for my decision. And then I stay on that decision, because it is a tested decision.

Kinda like Zen programming :)

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Assembler is not cross-platform, and with normal coding, it's also slower than C/C++, because the compilers do insane optimizations which nobody can do by hand in assembler.

 

..ill think twice about that..and also, remember that compiler is nothing but handwritten code..

  • Upvote 1

 

Link to comment
Share on other sites

..ill think twice about that..and also, remember that compiler is nothing but handwritten code..

Handwritten yes, but handwritten by many people. A single people can not do such optimizations by hand in assembler. Unless he writes a assembler optimizing language, which C and FORTRAN was made for - to have a faster assembler than hand written by a single person.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Guest Red Ocktober

question for the theoretical purist cool.png and everyone else...

 

do you'all consider it bad practice to have a virtual method actually do something... i mean, since by it's very nature it's designed merely to serve as a "placeholder" or "template" to be overridden?

 

thx

 

--Mike

Link to comment
Share on other sites

Virtual isn't just a "placeholder" or "template" and it can (but has not to) be overridden.

 

In my opinion, the strength of virtual methods is that you can decide which method to use (base, derived) or to define that an specific method has to be implemented by user ( = 0 ). And you can also hide informations from the user.

 

For example my actual engine iteration has something like this: (pseudo code, does not compile)

 

class IRenderDevice
{
 public:
  virtual void Initialise(u16 Width, u16 Height, u8 Bits) = 0;
  virtual void Draw(...) = 0;
  virtual void Terminate() = 0;
};

class D3D9RenderDevice : public IRenderDevice
{
 private:
  //some dx9 vars
  LPDIRECT3D9* d3d9;
  LPDIRECT3DDEVICE9* d3d9device;
 public:
  virtual void Initialise(u16 Width, u16 Height, u8 Bits);
  virtual void Draw(...);
  virtual void Terminate();
};

 

And this is how to use it:

int main(int argc, char** argv)
{
IRenderDevice* mydevice = new D3D9Device();
mydevice->Initialise(800,600,32);
// mainloop
while(true)
{
   mydevice->Draw(.....);
}
mydevice->Terminate();
};

 

So whats the advantage of this ? I just change "new D3D9Device();" with "new OpenGL33Device();" and it uses OpenGL 3.3.

To be honest, this is not the full implementation and style but you should get the idea behind this smile.png

 

In my real code the instances are created by a core class (you can call it factory) and the user of the "engine" has no worries but using directx / opengl by himself. He just uses my interface and classes i provide him.

 

Of course he could write his own RenderDevice by inheritancing my IRenderDevice and create an instance by himself "new myogrerenderer();" etc.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...