Sign in to follow this  
jeffreyp23

Problem with virtual function

Recommended Posts

Hi guys,

Let me post the code first :
[source]

#include <windows.h>
#include <iostream>
#include <vector>

class class1
{
public:
class1(){}

void VoegToe(const class1& t) { h.push_back(t); }
void PrintAlles(){
for(DWORD u=0;u<h.size();u++){
std::cout << "object : " << u << " waarde = ";
h[u].GetWaarde();
}
}

virtual void GetWaarde() { std::cout << "1" << std::endl; }

private:

std::vector<class1> h;

};

class class2 : public class1
{
public:
class2() {
waarde = 0;
}

void Init(int w, class1& hoof) {
waarde = w;
hoof.VoegToe(*this);
}

void GetWaarde() { std::cout << waarde << std::endl; }

private:
int waarde;

};

int main()
{
class1 test1;
class2 test2;

test2.Init(5, test1);
test1.PrintAlles();

system("pause");
return 0;
}

[/source]

The problem is that it prints 1 instead of 5,
and i cant figure out why it is not overriding the
[source] virtual void GetWaarde() { std::cout << "1" << std::endl; } [/source]
in the base class.

Thanks in advance

Share this post


Link to post
Share on other sites
You've sliced class2 by copying it into a class1 in the vector.

I presume this wasn't your intention as now you're holding a class1 not a class2 which will give you only the base classes methods.

Jans.

Share this post


Link to post
Share on other sites
[quote name='Jansic' timestamp='1318596248' post='4872498']
You've sliced class2 by copying it into a class1 in the vector.

I presume this wasn't your intention as now you're holding a class1 not a class2 which will give you only the base classes methods.

Jans.
[/quote]

Yea, i want to hold a class 2 into the vector not a class 1,
but how do i do this without a forward class declaration?
Because when i want to use it class 1 and class 2 are in 2 separate headers,
and class 2 will include class 1 so a forward declaration wont work then.
Or am i wrong?


Share this post


Link to post
Share on other sites
To avoid slicing, you must store some kind of reference, not a value. A typical solution is to store (smart) pointers, such as std::vector<std::shared_ptr<Example> >. Another option is to use a smart container, like boost::ptr_vector<Example>.

Share this post


Link to post
Share on other sites
Thanks i got it working now with this code :
[source]

#include <windows.h>
#include <iostream>
#include <vector>
#include <boost\ptr_container\ptr_container.hpp>

class class1
{
public:
class1(){}

void VoegToe(class1& t) { h.push_back(dynamic_cast<class1*>(&t)); }
void PrintAlles(){
for(DWORD u=0;u<h.size();u++){
std::cout << "object : " << u << " waarde = ";
h[u].GetWaarde();
}
}

virtual void GetWaarde() { std::cout << "1" << std::endl; }

private:

boost::ptr_vector<class1> h;

};

class class2 : public class1
{
public:
class2() {
waarde = 0;
}

void Init(int w, class1& hoof) {
waarde = w;
hoof.VoegToe(*this);
}

void GetWaarde() { std::cout << waarde << std::endl; }

private:
int waarde;

};

int main()
{
class1 test1;
class2 test2;

test2.Init(5, test1);
test1.PrintAlles();

system("pause");
return 0;
}
[/source]

But i have 2 questions left :
1. I use a dynamic cast to cast to a pointer, i read somewhere that casts are very expensive.
Is there any other way to do it without a cast?

2. When i exit the program is crashes with a debug assertion failed.
Expression : vector iterators incompatible.
Is this because it cant delete the pointer or something?

Share this post


Link to post
Share on other sites
[quote]
1. I use a dynamic cast to cast to a pointer, i read somewhere that casts are very expensive.
Is there any other way to do it without a cast?
[/quote]
In this case, you don't need a cast here - you are taking the address of a class1 reference, which yields a pointer-to-class1. In general, you do not need a cast to point a base pointer at an address of a derived instance.

Dynamic casts [u]can be[/u] expensive. Unfortunately, doing any runtime logic can be expensive. You must first profile and determine whether you've created a bottleneck. If so, you must design and implement a suitable replacement. Finally, you must profile again to ensure you've had a positive impact.

[quote]
2. When i exit the program is crashes with a debug assertion failed.
Expression : vector iterators incompatible.
Is this because it cant delete the pointer or something?
[/quote]
It is because you place a pointer to an automatically allocated object in a container that imposes ownership semantics. Essentially, boost::ptr_vector<> expects to be able to call [i]delete[/i] on the pointer you give it.

If your pointers do in fact outlive the container, then you could store raw pointers to them safely (for a given definition of "safely").

Otherwise here are a few options off the top of my head:
[list]
[*] Allocate the pointer using new, pass as a raw pointer (dangerous)
[*] Allocate the pointer using new or std::make_shared(), store in std::shared_ptr<>, container is of type std::vector<std::shared_ptr<> >
[*] Retain current interface, but use a virtual clone() member function to copy the object into the container.
[/list]

Share this post


Link to post
Share on other sites
Oke i did go with the second option :
[source]

#include <windows.h>
#include <iostream>
#include <vector>
#include <memory>

class class1
{
public:
class1(){}

void VoegToe(std::shared_ptr<class1> t) { h.push_back(t); }
void PrintAlles(){
for(DWORD u=0;u<h.size();u++){
std::cout << "object : " << u << " waarde = ";
h[u]->GetWaarde();
}
}

virtual void GetWaarde() { std::cout << "1" << std::endl; }

private:

std::vector<std::shared_ptr<class1>> h;

};

class class2 : public class1
{
public:
class2() {
waarde = 0;
}

void Init(int w, class1& hoof) {
waarde = w;
hoof.VoegToe(std::shared_ptr<class1>(std::make_shared<class1>(*this)));
}

void GetWaarde() { std::cout << waarde << std::endl; }

private:
int waarde;

};

int main()
{
class1 test1;
class2 test2;

test2.Init(5, test1);
test1.PrintAlles();

system("pause");
return 0;
}
[/source]

But it seems the object get sliced again?
Because the output is 1 instead of 5.

Thanks for all the help already : )

Share this post


Link to post
Share on other sites
Your call to make_shared() is slicing the object again.

Something like this:
[code]
#include <windows.h>
#include <iostream>
#include <vector>
#include <memory>

class class1
{
public:
class1(){}

void VoegToe(std::shared_ptr<class1> t) {
h.push_back(t);
}

void PrintAlles() {
for(DWORD u = 0 ; u < h.size() ; u++){
std::cout << "object : " << u << " waarde = ";
h[u]->GetWaarde();
}
}

virtual void GetWaarde() {
std::cout << "1" << std::endl;
}

private:
std::vector<std::shared_ptr<class1>> h;
};

class class2 : public class1
{
public:
class2() {
waarde = 0;
}

void Init(int w) {
waarde = w;
}

void GetWaarde() {
std::cout << waarde << std::endl;
}

private:
int waarde;
};

int main()
{
class1 test1;

std::shared_ptr<class2> test2 = std::make_shared<class2>();
test2->Init(5);

test1.VoegToe(test2);
test1.PrintAlles();

system("pause");
return 0;
}

[/code]
Getting a shared_ptr<> to "this" is tricky. There is a helper class, enable_shared_from_this(), but I think the above solution is better.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this