Problem with virtual function

Started by
7 comments, last by jeffreyp23 12 years, 6 months ago
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.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
Advertisement
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.

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.


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?


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

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 can be 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.


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 delete 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:

  • 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.
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->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 : )
Your call to make_shared() is slicing the object again.

Something like this:

#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->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;
}


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.
Oke, thanks for all your help : )

This topic is closed to new replies.

Advertisement