The following code, prints out
Derived
Base
Base
But I need every Derived object put into User::items, call its own print function, but not the base class one. Can I achieve that without using pointers? If it is not possible, how should I write the function that deletes User::items one by one and frees memory, so that there should not be any memory leaks?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Base{
public:
virtual void print(){ cout << "Base" << endl;}
};
class Derived: public Base{
public:
void print(){ cout << "Derived" << endl;}
};
class User{
public:
vector<Base> items;
void add_item( Base& item ){
item.print();
items.push_back( item );
items.back().print();
}
};
void fill_items( User& u ){
Derived d;
u.add_item( d );
}
int main(){
User u;
fill_items( u );
u.items[0].print();
}
You need to use pointers, and you need to give your base class a virtual destructor. The destructor does not have to do anything, but it must exist. Your add function then looks like:
void add_item( Base * item ){
item->print();
items.push_back( item );
}
where items is a vector<Base *>. To destroy the items (assuming a virtual destructor):
for( int i = 0; i < items.size(); i++ ) {
delete items[i];
}
items.clear();
You need a virtual destructor for base to make sure objects of type Derived get destroyed properly when calling delete on a pointer of type Base.
class Base{
public:
virtual void print(){ cout << "Base" << endl;}
virtual ~Base( ) { } // virtual destructor
};
Then you can use Boosts ptr_vector to store pointers to your objects that get deleted when the container gets destroyed.
just explaining:
In order to understand what is going on, you may try to define class Base abstract (e.g. defining any method pure virtual). In this case I expect you'll see compiler errors.
This way you'll recognize what vector actually does: it creates new instances of class Base by means of copy construction when you push_back( derived ).
This is why you want to use pointers instead. Then vector works with your originally created objects of type Derived instead of own copies of type Base.
Related
I would like to store instances of several classes in a vector. Since all classes inherit from the same base class this should be possible.
Imagine this program:
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Derived derived;
vector<Base> vect;
vect.push_back(derived);
vect[0].identify();
return 0;
}
I expected it to print "DERIVED", because the identify() method is virtual. Instead vect[0] seems to be a Base instance and it prints "BASE".
I guess I could write my own container (probably derived from vector) somehow that is capable of doing this (maybe holding only pointers...).
I just wanted to ask if there is a more C++'ish way to do this. AND I would like to be completely vector-compatible (just for convenience if other users should ever use my code).
What you are seeing is Object Slicing.
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.
Solution:
You should store pointer to object of Base class in the vector:
vector<Base*>
By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.
Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.
You're experiencing slicing. The vector copies the derived object, a new one of type Base is inserted.
TL;DR: You should not inherit from a publicly copyable/movable class.
It is actually possible to prevent object slicing, at compilation time: the base object should not be copyable in this context.
Case 1: an abstract base
If the base is abstract, then it cannot be instantiated and thus you cannot experience slicing.
Case 2: a concrete base
If the base is not abstract, then it can be copied (by default). You have two choices:
prevent copy altogether
allow copy only for children
Note: in C++11, the move operations cause the same issue.
// C++ 03, prevent copy
class Base {
public:
private:
Base(Base const&);
void operator=(Base const&);
};
// C++ 03, allow copy only for children
class Base {
public:
protected:
Base(Base const& other) { ... }
Base& operator=(Base const& other) { ...; return *this; }
};
// C++ 11, prevent copy & move
class Base {
public:
Base(Base&&) = delete;
Base(Base const&) = delete;
Base& operator=(Base) = delete;
};
// C++ 11, allow copy & move only for children
class Base {
public:
protected:
Base(Base&&) = default;
Base(Base const&) = default;
Base& operator=(Base) = default;
};
I'd use vector<Base*> to store them. If you say vector<Base>, slicing will occur.
This does mean that you'd have to delete the actual objects yourself after you've removed the pointers from your vector, but otherwise you should be fine.
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived
pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}
As all others mentioned here, you cannot accomplish to insert an object of derived to a vector of base due to object slicing that would happen on copy construction.
If goal is to avoid memory allocation, you can use an std::variant, but the vector would no longer be of base class
using HierarchyItem = std::variant<Base, Derived>;
int main()
{
vector<HierarchyItem> vect;
vect.push_back(Derived());
std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);
return 0;
}
I would like to store instances of several classes in a vector. Since all classes inherit from the same base class this should be possible.
Imagine this program:
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Derived derived;
vector<Base> vect;
vect.push_back(derived);
vect[0].identify();
return 0;
}
I expected it to print "DERIVED", because the identify() method is virtual. Instead vect[0] seems to be a Base instance and it prints "BASE".
I guess I could write my own container (probably derived from vector) somehow that is capable of doing this (maybe holding only pointers...).
I just wanted to ask if there is a more C++'ish way to do this. AND I would like to be completely vector-compatible (just for convenience if other users should ever use my code).
What you are seeing is Object Slicing.
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.
Solution:
You should store pointer to object of Base class in the vector:
vector<Base*>
By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.
Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.
You're experiencing slicing. The vector copies the derived object, a new one of type Base is inserted.
TL;DR: You should not inherit from a publicly copyable/movable class.
It is actually possible to prevent object slicing, at compilation time: the base object should not be copyable in this context.
Case 1: an abstract base
If the base is abstract, then it cannot be instantiated and thus you cannot experience slicing.
Case 2: a concrete base
If the base is not abstract, then it can be copied (by default). You have two choices:
prevent copy altogether
allow copy only for children
Note: in C++11, the move operations cause the same issue.
// C++ 03, prevent copy
class Base {
public:
private:
Base(Base const&);
void operator=(Base const&);
};
// C++ 03, allow copy only for children
class Base {
public:
protected:
Base(Base const& other) { ... }
Base& operator=(Base const& other) { ...; return *this; }
};
// C++ 11, prevent copy & move
class Base {
public:
Base(Base&&) = delete;
Base(Base const&) = delete;
Base& operator=(Base) = delete;
};
// C++ 11, allow copy & move only for children
class Base {
public:
protected:
Base(Base&&) = default;
Base(Base const&) = default;
Base& operator=(Base) = default;
};
I'd use vector<Base*> to store them. If you say vector<Base>, slicing will occur.
This does mean that you'd have to delete the actual objects yourself after you've removed the pointers from your vector, but otherwise you should be fine.
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived
pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}
As all others mentioned here, you cannot accomplish to insert an object of derived to a vector of base due to object slicing that would happen on copy construction.
If goal is to avoid memory allocation, you can use an std::variant, but the vector would no longer be of base class
using HierarchyItem = std::variant<Base, Derived>;
int main()
{
vector<HierarchyItem> vect;
vect.push_back(Derived());
std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);
return 0;
}
I would like to store instances of several classes in a vector. Since all classes inherit from the same base class this should be possible.
Imagine this program:
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Derived derived;
vector<Base> vect;
vect.push_back(derived);
vect[0].identify();
return 0;
}
I expected it to print "DERIVED", because the identify() method is virtual. Instead vect[0] seems to be a Base instance and it prints "BASE".
I guess I could write my own container (probably derived from vector) somehow that is capable of doing this (maybe holding only pointers...).
I just wanted to ask if there is a more C++'ish way to do this. AND I would like to be completely vector-compatible (just for convenience if other users should ever use my code).
What you are seeing is Object Slicing.
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.
Solution:
You should store pointer to object of Base class in the vector:
vector<Base*>
By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.
Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.
You're experiencing slicing. The vector copies the derived object, a new one of type Base is inserted.
TL;DR: You should not inherit from a publicly copyable/movable class.
It is actually possible to prevent object slicing, at compilation time: the base object should not be copyable in this context.
Case 1: an abstract base
If the base is abstract, then it cannot be instantiated and thus you cannot experience slicing.
Case 2: a concrete base
If the base is not abstract, then it can be copied (by default). You have two choices:
prevent copy altogether
allow copy only for children
Note: in C++11, the move operations cause the same issue.
// C++ 03, prevent copy
class Base {
public:
private:
Base(Base const&);
void operator=(Base const&);
};
// C++ 03, allow copy only for children
class Base {
public:
protected:
Base(Base const& other) { ... }
Base& operator=(Base const& other) { ...; return *this; }
};
// C++ 11, prevent copy & move
class Base {
public:
Base(Base&&) = delete;
Base(Base const&) = delete;
Base& operator=(Base) = delete;
};
// C++ 11, allow copy & move only for children
class Base {
public:
protected:
Base(Base&&) = default;
Base(Base const&) = default;
Base& operator=(Base) = default;
};
I'd use vector<Base*> to store them. If you say vector<Base>, slicing will occur.
This does mean that you'd have to delete the actual objects yourself after you've removed the pointers from your vector, but otherwise you should be fine.
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived
pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}
As all others mentioned here, you cannot accomplish to insert an object of derived to a vector of base due to object slicing that would happen on copy construction.
If goal is to avoid memory allocation, you can use an std::variant, but the vector would no longer be of base class
using HierarchyItem = std::variant<Base, Derived>;
int main()
{
vector<HierarchyItem> vect;
vect.push_back(Derived());
std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);
return 0;
}
I would like to store instances of several classes in a vector. Since all classes inherit from the same base class this should be possible.
Imagine this program:
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Derived derived;
vector<Base> vect;
vect.push_back(derived);
vect[0].identify();
return 0;
}
I expected it to print "DERIVED", because the identify() method is virtual. Instead vect[0] seems to be a Base instance and it prints "BASE".
I guess I could write my own container (probably derived from vector) somehow that is capable of doing this (maybe holding only pointers...).
I just wanted to ask if there is a more C++'ish way to do this. AND I would like to be completely vector-compatible (just for convenience if other users should ever use my code).
What you are seeing is Object Slicing.
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.
Solution:
You should store pointer to object of Base class in the vector:
vector<Base*>
By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.
Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.
You're experiencing slicing. The vector copies the derived object, a new one of type Base is inserted.
TL;DR: You should not inherit from a publicly copyable/movable class.
It is actually possible to prevent object slicing, at compilation time: the base object should not be copyable in this context.
Case 1: an abstract base
If the base is abstract, then it cannot be instantiated and thus you cannot experience slicing.
Case 2: a concrete base
If the base is not abstract, then it can be copied (by default). You have two choices:
prevent copy altogether
allow copy only for children
Note: in C++11, the move operations cause the same issue.
// C++ 03, prevent copy
class Base {
public:
private:
Base(Base const&);
void operator=(Base const&);
};
// C++ 03, allow copy only for children
class Base {
public:
protected:
Base(Base const& other) { ... }
Base& operator=(Base const& other) { ...; return *this; }
};
// C++ 11, prevent copy & move
class Base {
public:
Base(Base&&) = delete;
Base(Base const&) = delete;
Base& operator=(Base) = delete;
};
// C++ 11, allow copy & move only for children
class Base {
public:
protected:
Base(Base&&) = default;
Base(Base const&) = default;
Base& operator=(Base) = default;
};
I'd use vector<Base*> to store them. If you say vector<Base>, slicing will occur.
This does mean that you'd have to delete the actual objects yourself after you've removed the pointers from your vector, but otherwise you should be fine.
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived
pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}
As all others mentioned here, you cannot accomplish to insert an object of derived to a vector of base due to object slicing that would happen on copy construction.
If goal is to avoid memory allocation, you can use an std::variant, but the vector would no longer be of base class
using HierarchyItem = std::variant<Base, Derived>;
int main()
{
vector<HierarchyItem> vect;
vect.push_back(Derived());
std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);
return 0;
}
I would like to store instances of several classes in a vector. Since all classes inherit from the same base class this should be possible.
Imagine this program:
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Derived derived;
vector<Base> vect;
vect.push_back(derived);
vect[0].identify();
return 0;
}
I expected it to print "DERIVED", because the identify() method is virtual. Instead vect[0] seems to be a Base instance and it prints "BASE".
I guess I could write my own container (probably derived from vector) somehow that is capable of doing this (maybe holding only pointers...).
I just wanted to ask if there is a more C++'ish way to do this. AND I would like to be completely vector-compatible (just for convenience if other users should ever use my code).
What you are seeing is Object Slicing.
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.
Solution:
You should store pointer to object of Base class in the vector:
vector<Base*>
By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.
Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.
You're experiencing slicing. The vector copies the derived object, a new one of type Base is inserted.
TL;DR: You should not inherit from a publicly copyable/movable class.
It is actually possible to prevent object slicing, at compilation time: the base object should not be copyable in this context.
Case 1: an abstract base
If the base is abstract, then it cannot be instantiated and thus you cannot experience slicing.
Case 2: a concrete base
If the base is not abstract, then it can be copied (by default). You have two choices:
prevent copy altogether
allow copy only for children
Note: in C++11, the move operations cause the same issue.
// C++ 03, prevent copy
class Base {
public:
private:
Base(Base const&);
void operator=(Base const&);
};
// C++ 03, allow copy only for children
class Base {
public:
protected:
Base(Base const& other) { ... }
Base& operator=(Base const& other) { ...; return *this; }
};
// C++ 11, prevent copy & move
class Base {
public:
Base(Base&&) = delete;
Base(Base const&) = delete;
Base& operator=(Base) = delete;
};
// C++ 11, allow copy & move only for children
class Base {
public:
protected:
Base(Base&&) = default;
Base(Base const&) = default;
Base& operator=(Base) = default;
};
I'd use vector<Base*> to store them. If you say vector<Base>, slicing will occur.
This does mean that you'd have to delete the actual objects yourself after you've removed the pointers from your vector, but otherwise you should be fine.
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual void identify ()
{
cout << "BASE" << endl;
}
};
class Derived: public Base
{
public:
virtual void identify ()
{
cout << "DERIVED" << endl;
}
};
int main ()
{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived
pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}
As all others mentioned here, you cannot accomplish to insert an object of derived to a vector of base due to object slicing that would happen on copy construction.
If goal is to avoid memory allocation, you can use an std::variant, but the vector would no longer be of base class
using HierarchyItem = std::variant<Base, Derived>;
int main()
{
vector<HierarchyItem> vect;
vect.push_back(Derived());
std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);
return 0;
}