I have a parent DataType class from which I inherit Data Type Int, DataType Double, DataTypeEnum
and CDataTypeStruct. Somewhere I use the print () method defined by the parent and somewhere I rewrite it. I call the print method using the << operator.
Why, when I call a title for the CDataTypeEnum type, everything is displayed correctly, as I have the print defined in the CDaraTypeEnum.
I get this
struct {
int int enum}
but if I want to list cout << structure << endl; so for the CDataTypeStruct type, I don't get an overloaded print method for each object?
Just to make the statement look like this
struct {
int int
enum {
NEW,
FIXED,
BROKEN,
DEAD
}
}
--
All program https://onecompiler.com/cpp/3y2rhbm7a and here's a minimal reproducible example:
#include <iostream>
#include <list>
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <vector>
using namespace std;
class CDataType
{
public:
CDataType(string type, size_t size);
friend ostream& operator << (ostream &os, CDataType &x);
virtual ostream& print (ostream &os) const;
protected:
string m_Type;
size_t m_Size;
};
CDataType::CDataType(string type, size_t size)
: m_Type(type),
m_Size(size)
{
}
ostream& operator << (ostream &os, CDataType &x)
{
x.print(os);
return os;
}
ostream& CDataType::print (ostream &os) const
{
os << m_Type;
return os;
}
class CDataTypeInt : public CDataType
{
public:
CDataTypeInt();
};
CDataTypeInt::CDataTypeInt()
: CDataType("int", 4)
{
}
class CDataTypeEnum : public CDataType
{
public:
CDataTypeEnum();
CDataTypeEnum& add(string x);
virtual ostream& print (ostream &os) const;
protected:
vector<string> listEnums;
set<string> listEnumsNames;
};
CDataTypeEnum::CDataTypeEnum()
: CDataType("enum", 4)
{
}
ostream& CDataTypeEnum::print(ostream &os) const
{
os << m_Type << "{\n";
for (auto i=listEnums.begin(); i != listEnums.end(); ++i )
{
os << *i;
if(i != listEnums.end()-1)
{
os << ",";
}
os << "\n";
}
os << "}";
return os;
}
CDataTypeEnum& CDataTypeEnum::add(string x)
{
if(listEnumsNames.find(x) == listEnumsNames.end())
{
listEnums.push_back(x);
listEnumsNames.emplace(x);
}
else
cout << "vyjimkaa" << endl;
// CSyntaxException e("Duplicate enum value: " + x);
return *this;
}
class CDataTypeStruct : public CDataType
{
public:
virtual ostream& print (ostream &os) const;
CDataTypeStruct();
CDataTypeStruct& addField(const string &name, const CDataType &type);
protected:
list<unique_ptr<CDataType>> m_Field;
unordered_set<string> m_Field_names;
};
CDataTypeStruct::CDataTypeStruct()
:CDataType("struct", 0)
{
}
CDataTypeStruct& CDataTypeStruct::addField(const string &name, const CDataType &type)
{
if( m_Field_names.find(name) == m_Field_names.end() )
{
m_Field.push_back(make_unique<CDataType>(type));
m_Field_names.emplace(name);
}
// else
//throw CSyntaxException("Duplicate field: " + name);
return *this;
}
ostream& CDataTypeStruct::print (ostream &os) const
{
os << m_Type << "{\n";
for(const auto &uptr : m_Field)
{
uptr->print(os) << " " /*<< "{\n"*/;
}
os << "}";
return os;
}
int main()
{
CDataTypeInt inta;
CDataTypeInt intb;
CDataTypeStruct struktura;
CDataTypeEnum enumos;
enumos.add( "NEW" ).add ( "FIXED" ) .add ( "BROKEN" ) .add ( "DEAD" );
struktura.addField("integera", inta);
struktura.addField("integerb", intb);
struktura.addField("bbb", enumos);
cout << enumos << endl;
cout << struktura << endl;
}```
As I already suggested in a similar question you posted already (and deleted), you're again slicing here:
struktura.addField("integera", inta); // Slicing
struktura.addField("integerb", intb); // Slicing
struktura.addField("bbb", enumos); // Slicing
Again, consider following guideline "A polymorphic class should suppress public copy/move" from the C++ Core Guidelines. To do this:
class CDataType {
public:
// ...
// Disable move and copy
CDataType(CDataType const &) = delete;
CDataType(CDataType &&) = delete;
CDataType& operaror=(CDataType const &) = delete;
CDataType& operaror=(CDataType &&) = delete;
// Dtor should be virtual.
virtual ~CDataType() = default;
// ...
};
Then, adapt your code accordingly (as it won't compile anymore).
Also, The destructor of CDataType should be virtual.
Edit: Please consider the following example, which hopeful makes the slicing issue clearer:
#include <cstdio>
#include <list>
#include <memory>
struct A {
A() = default;
A(A const&) { std::puts("A(A const&)"); }
virtual ~A() { std::puts("~A()"); }
};
struct B : public A {
B() = default;
B(B const&) { std::puts("B(B const&)"); }
~B() override { std::puts("~B()"); }
};
void slicing_addField(A const& a) {
std::puts("slicing_f");
std::list<std::unique_ptr<A>> l;
l.push_back(std::make_unique<A>(a));
}
void non_slicing_addField(std::unique_ptr<A> a) {
std::puts("non_slicing_f");
std::list<std::unique_ptr<A>> l;
l.push_back(std::move(a));
}
int main() {
// This is what you do
slicing_addField(B{});
// This is how you may solve
non_slicing_addField(std::make_unique<B>());
}
Output:
slicing_f
A(A const&)
~A()
~B()
~A()
non_slicing_f
~B()
~A()
As you can see from the output, you're only calling A(A const&) in slicing_addField.
This means the call to make_unique<A>(a) is allocating an object of run-time type A (while you want it of run-time type B).
The main problem is that you try to copy the CDataType decendant in addField by using make_unique<CDataType>, but that actually creates a CDataType, not a CDataTypeInt, CDataTypeEnum or CDataTypeStruct.
Since constructors can't be virtual (in standard C++) you need to create a separate virtual function to do the copying. A common name for such a function is clone. Example:
class CDataType {
public:
CDataType(string type, size_t size);
CDataType(const CDataType&) = delete;
CDataType& operator=(const CDataType&) = delete;
virtual ~CDataType() = default; // add virtual dtor
virtual std::unique_ptr<CDataType> clone() const {
return std::make_unique<CDataType>(m_Type, m_Size);
}
// ...
};
class CDataTypeEnum : public CDataType {
public:
using CDataType::CDataType;
std::unique_ptr<CDataType> clone() const override {
// use make_unique to create the correct type:
auto np = std::make_unique<CDataTypeEnum>(m_Type, m_Size);
np->listEnums = listEnums;
np->listEnumsNames = listEnumsNames;
return np;
}
// ...
};
class CDataTypeStruct : public CDataType {
public:
using CDataType::CDataType;
std::unique_ptr<CDataType> clone() const override {
// use make_unique to create the correct type:
auto np = std::make_unique<CDataTypeStruct>(m_Type, m_Size);
np->m_Field_names = m_Field_names;
for(auto& cdtp : m_Field)
np->m_Field.emplace_back(cdtp->clone()); // use clone here
return np;
}
// ...
};
Then addField would look like this:
CDataTypeStruct& CDataTypeStruct::addField(const string& name,
const CDataType& type) {
if (m_Field_names.find(name) == m_Field_names.end()) {
m_Field.emplace_back(type.clone()); // and use clone here
m_Field_names.emplace(name);
}
return *this;
}
Related
Hi Stack Overflow Community !
I am working on a project that heavily uses the interesting nlohmann_json library and it appears that I need to add an inheritance link on a specific class, which objects are serialized at one moment.
I tried different advice found on the github Issues page of the library, but can't make it work.
Here is an dummy code I tried :
#include <nlohmann/json.hpp>
#include <iostream>
#include <memory>
#include <vector>
using json = nlohmann::json;
namespace nlohmann {
template <typename T>
struct adl_serializer<std::unique_ptr<T>> {
static void to_json(json& j, const std::unique_ptr<T>& opt) {
if (opt) {
j = *opt.get();
} else {
j = nullptr;
}
}
};
}
class Base {
public:
Base() = default;
virtual ~Base() = default;
virtual void foo() const { std::cout << "Base::foo()" << std::endl; }
};
class Obj : public Base
{
public:
Obj(int i) : _i(i) {}
void foo() const override { std::cout << "Obj::foo()" << std::endl; }
int _i = 0;
friend std::ostream& operator<<(std::ostream& os, const Obj& o);
};
std::ostream& operator<<(std::ostream& os, const Base& o)
{
os << "Base{} ";
return os;
}
std::ostream& operator<<(std::ostream& os, const Obj& o)
{
os << "Obj{"<< o._i <<"} ";
return os;
}
void to_json(json& j, const Base& b)
{
std::cout << "called to_json for Base" << std::endl;
}
void to_json(json& j, const Obj& o)
{
std::cout << "called to_json for Obj" << std::endl;
}
int main()
{
std::vector<std::unique_ptr<Base>> v;
v.push_back(std::make_unique<Base>());
v.push_back(std::make_unique<Obj>(5));
v.push_back(std::make_unique<Base>());
v.push_back(std::make_unique<Obj>(10));
std::cout << v.size() << std::endl;
json j = v;
}
// Results in :
// Program returned: 0
// 4
// called to_json for Base
// called to_json for Base
// called to_json for Base
// called to_json for Base
(https://gcc.godbolt.org/z/dc8h8f)
I understand that the adl_serializer only get the type Base when called, but I don't see how to make him aware of the type Obj as well...
Does anyone see what I am missing here ?
Thanks in advance for your advice and help !
nlohmann.json does not include polymorphic serializing, but you can implement it yourself in a specialized adl_serializer. Here we're storing and checking an additional _type JSON field, used as a key to map to pairs of type-erased from/to functions for each derived type.
namespace PolymorphicJsonSerializer_impl {
template <class Base>
struct Serializer {
void (*to_json)(json &j, Base const &o);
void (*from_json)(json const &j, Base &o);
};
template <class Base, class Derived>
Serializer<Base> serializerFor() {
return {
[](json &j, Base const &o) {
return to_json(j, static_cast<Derived const &>(o));
},
[](json const &j, Base &o) {
return from_json(j, static_cast<Derived &>(o));
}
};
}
}
template <class Base>
struct PolymorphicJsonSerializer {
// Maps typeid(x).name() to the from/to serialization functions
static inline std::unordered_map<
char const *,
PolymorphicJsonSerializer_impl::Serializer<Base>
> _serializers;
template <class... Derived>
static void register_types() {
(_serializers.emplace(
typeid(Derived).name(),
PolymorphicJsonSerializer_impl::serializerFor<Base, Derived>()
), ...);
}
static void to_json(json &j, Base const &o) {
char const *typeName = typeid(o).name();
_serializers.at(typeName).to_json(j, o);
j["_type"] = typeName;
}
static void from_json(json const &j, Base &o) {
_serializers.at(j.at("_type").get<std::string>().c_str()).from_json(j, o);
}
};
Usage:
// Register the polymorphic serializer for objects derived from `Base`
namespace nlohmann {
template <>
struct adl_serializer<Base>
: PolymorphicJsonSerializer<Base> { };
}
// Implement `Base`'s from/to functions
void to_json(json &, Base const &) { /* ... */ }
void from_json(json const &, Base &) { /* ... */ }
// Later, implement `Obj`'s from/to functions
void to_json(json &, Obj const &) { /* ... */ }
void from_json(json const &, Obj &) { /* ... */ }
// Before any serializing/deserializing of objects derived from `Base`, call the registering function for all known types.
PolymorphicJsonSerializer<Base>::register_types<Base, Obj>();
// Works!
json j = v;
Caveats:
typeid(o).name() is unique in practice, but is not guaranteed to be by the standard. If this is an issue, it can be replaced with any persistent runtime type identification method.
Error handling has been left out, though _serializers.at() will throw std::out_of_range when trying to serialize an unknown type.
This implementation requires that the Base type implements its serialization with ADL from/to functions, since it takes over nlohmann::adl_serializer<Base>.
See it live on Wandbox
I have an abstract class named Pet and three derived classes Cat Dog Goldfish. I am trying to store them in vector and print the pets names. But I am getting some hexadecimal number. I don't know where I am doing wrong.
// Here is pet.h
#ifndef PET_H
#define PET_H
#include<iostream>
using namespace std;
class Pet
{
public:
//constructor
Pet(string);
// virtual destructor
virtual ~Pet();
// pure virtual function
virtual string speak() = 0;
//getters setters
string getName();
//overloading comparative operators
bool operator< (Pet&);
friend ostream& operator<<(ostream&, Pet&);
protected:
string pet_name; //name of pet
};
#endif // PET_H
Here is pet.cpp
#include <iostream>
#include "Pet.hpp"
#include <string>
using namespace std;
//constructor
Pet::Pet(string name) : pet_name(name)
{}
//overloading comparator function
bool Pet::operator<(Pet& obj)
{
return ((pet_name.compare(obj.pet_name)) < 0);
}
//getter for name
string Pet::getName() { return pet_name; }
// destructor
Pet::~Pet() { /* dtor */ }
ostream& operator<<(ostream& output, Pet& p) {
output << "I am pet";
return output;
}
#ifndef CAT_H
#define CAT_H
#include "Pet.hpp"
class Cat: public Pet
{
public:
Cat(string);
virtual ~Cat();
string speak();
friend ostream& operator<<(ostream &, Cat&);
};
#endif // CAT_H
#include "Cat.hpp"
#include<string>
Cat::Cat(string name):Pet(name)
{
//ctor
}
string Cat::speak()
{
return ">>Meow Meow>>";
}
Cat::~Cat()
{
//dtor
}
ostream& operator<<(ostream& output, Cat& p) {
output << "I am " << p.getName() << " " << p.speak() << endl;
return output;
}
List.hpp File
#ifndef LIST_H
#define LIST_H
#include<iostream>
#include <vector>
#include<algorithm>
using namespace std;
template<class T>
class List
{
public:
void add_item(T);
void sortList();
void print();
private:
vector<T> list;
};
template<class T>
void List<T>::add_item(T item_list) {
list.push_back(item_list);
}
template<class T>
void List<T>::sortList() {
sort(list.begin(), list.end());
}
template<class T>
void List<T>::print() {
std::vector<T>::iterator i;
for (i = list.begin(); i != list.end(); ++i) {
cout << *i << endl;
}
}
#endif // LIST_H
And this is the main function
int main()
{
List<Pet*> pets;
// book items adding in the lists
pets.add_item(new Cat("Kitty"));
pets.add_item(new Cat("Tom"));
// sorting lists
pets.sortList();
// printing lists
// ----- Here is the PROBLEM ------
pets.print(); // --> Here I am getting problem
// --> when this statement executes, I get the hexa decimal number
// saving in files
return 0;
}
ftfy:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <memory>
class Pet
{
public:
Pet(std::string name) : name{ std::move(name) } {}
virtual ~Pet() = default;
std::string get_name() const { return name; }
virtual std::string speak() const = 0;
friend std::ostream& operator<<(std::ostream& output, Pet const &pet)
{
output << "I am pet named " << pet.name;
return output;
}
protected:
std::string name;
};
class Cat : public Pet
{
public:
Cat(std::string name) : Pet{ std::move(name) } {};
virtual ~Cat() = default;
virtual std::string speak() const override { return ">>Meow Meow>>"; }
friend std::ostream& operator<<(std::ostream &output, Cat const & cat)
{
output << "I am a cat named " << cat.name << ' ' << cat.speak();
return output;
}
};
class Dog : public Pet
{
public:
Dog(std::string name) : Pet{ std::move(name) } {};
virtual ~Dog() = default;
virtual std::string speak() const override { return ">>Woof Woof>>"; }
friend std::ostream& operator<<(std::ostream &output, Dog const & dog)
{
output << "I am a dog named " << dog.name << ' ' << dog.speak();
return output;
}
};
template<class T>
class List
{
public:
void add_item(T item_list) { list.push_back(item_list); }
void sortList() {
std::sort(list.begin(), list.end(),
[](T const &lhs, T const &rhs) -> bool { return lhs.get()->get_name() < rhs.get()->get_name(); });
}
void print() const
{
for (typename std::vector<T>::const_iterator i{ list.begin() }; i != list.end(); ++i) {
if (auto cat = dynamic_cast<Cat*>((*i).get()); cat)
std::cout << *cat;
else if (auto dog = dynamic_cast<Dog*>((*i).get()); dog)
std::cout << *dog;
else
std::cout << **i;
std::cout.put('\n');
}
}
private:
std::vector<T> list;
};
int main()
{
List<std::shared_ptr<Pet>> pets;
pets.add_item(std::make_shared<Cat>("Kitty"));
pets.add_item(std::make_shared<Cat>("Tom"));
pets.add_item(std::make_shared<Dog>("Suzy"));
pets.add_item(std::make_shared<Dog>("Hasso"));
pets.sortList();
pets.print();
}
Every example that I've seen which implement the Rule of Five, implements the rule on a class without inheritance and polymorphism (virtual functions).
How can the rule of 5 be applied to the sample code bellow?
The test example uses c-arrays for dynamic objects. This is slightly contrived of course in order to have dynamic objects to manage to demonstrate the Rule of Five. In practice, it could have been anything (FILE ptrs, database handles, etc). Therefore, don't suggest using vector, or turning the exercise into demonstration of the Rule of Zero.
I'd prefer the solution not to use the copy-swap idiom because I find the non-copy-swap method more explicit in the case of non-inheriting classes. But if one wished to illustrate both methods, that would also be great.
Notes about sample code:
B inherits from A.
Both A & B have their own dynamic object to manage (a contrived c-array).
Class T is a contrived class to have custom type for diagnostic prints. The c-arrays store T elements.
Class A & B are void of Rule of Five implementation (to be defined by answer)
The c-arrays have not yet been allocated in the sample code, but of course, they would be at construction based on respective bufferSize.
Sample Code
//TEST TYPE
class T
{
public:
T() { cout << "ctorT" << endl; }
~T() { cout << "dtorT" < endl; }
T(const T& src) { cout << "copy-ctorT " << endl; }
T& operator=(const T& rhs) { cout << "copy-assignT" << endl; return *this; }
T(T&& src) noexcept { cout << "move-ctorT " << endl; }
T& operator=(T&& rhs) { cout << "move-assignT" << endl; return *this; }
}
class A
{
string nameParent = "A";
size_t bufferSizeParent = 0;
T *pBufferParent = nullptr; //c-array
public:
A(string tNameParent, size_t tBufferSizeParent) : nameParent(tNameParent), bufferSizeParent(tBufferSizeParent)
{
cout << "ctorA " << nameParent << endl;
}
virtual ~A(){ cout << "dtorA " << nameParent << endl; }
virtual string getName() { return nameParent; }
virtual void setName(const string name) { nameParent = name; }
};
class B : public A
{
string nameChild = "B";
size_t bufferSizeChild = 0;
T *pBufferChild = nullptr; //c-array
public:
B(string tNameChild, string tNameParent, size_t tBufferSizeChild, size_t tBufferSizeParent)
: A(tNameParent, tBufferSizeParent), nameChild(tNameChild), bufferSizeChild(tBufferSizeChild)
{
cout << "ctorB " << nameChild << endl;
}
~B(){ cout << "dtorB " << nameChild << endl; }
virtual string getName() override final { return nameChild; }
virtual void setName(const string name) override final { nameChild = name; }
};
A good rule of thumb is that a class should directly manage at most one resource. Other classes have all defaulted special members, preferably implicitly, but explicitly if they are to be declared in some variation.
Your example becomes
struct T;
extern T acquire_T();
extern void release_T(T);
class THandle
{
T handle;
public:
THandle()
: handle(acquire_T()) {}
~THandle() { release_T(handle); }
THandle(const THandle &) = delete;
THandle(THandle && other)
: handle(other.handle)
{ other.handle = {}; }
THandle & operator=(const THandle &) = delete;
THandle & operator=(THandle && other)
{ std::swap(handle, other.handle); }
}
class A
{
std::string nameParent = "A";
std::size_t bufferSizeParent = 0;
THandle tParent;
public:
A() {}
A(std::string tNameParent, std::size_t tBufferSizeParent) : nameParent(tNameParent), bufferSizeParent(tBufferSizeParent)
{ }
virtual ~A() = default;
A(const A &) = default;
A(A &&) = default;
A & operator=(const A &) = default;
A & operator=(A &&) = default;
virtual string getName() { return nameParent; }
virtual void setName(const string name) { nameParent = name; }
};
class B : public A
{
std::string nameChild = "B";
std::size_t bufferSizeChild = 0;
THandle tChild;
public:
B() {}
B(string tNameChild, string tNameParent, size_t tBufferSizeChild, size_t tBufferSizeParent)
: A(tNameParent, tBufferSizeParent), nameChild(tNameChild), bufferSizeChild(tBufferSizeChild), bufferChild(std::make_unique(tBufferSizeChild))
{ }
virtual string getName() override final { return nameChild; }
virtual void setName(const string name) override final { nameChild = name; }
};
I was expecting the assignment operator to be dynamically bound depending on the argument type:
struct NumberObject;
struct NumberString;
struct DataType
{
virtual void operator=(DataType& rhs) {};
virtual void operator=(NumberString& rhs) {};
};
struct NumberObject : DataType
{
double data = 7;
void operator=(DataType& rhs) override
{
std::cout << "Copying from DataType\n";
// The assignment operator that's called is this one. I was hoping it would be the one below.
}
void operator=(NumberString& rhs) override
{
std::cout << "Copying from NumberString\n";
}
};
struct NumberString : DataType
{
std::string data = "7";
void operator=(NumberString& rhs) override{};
};
int main()
{
DataType *pDataType1, *pDataType2;
pDataType1 = new NumberObject;
pDataType2 = new NumberString;
*pDataType1 = *pDataType2; // Both pointers are to DataType, I was hoping that the assignment operator taking
// NumberString as the right hand side would be called
return 0;
}
From this I guess that C++ can't dynamically bind functions based on the argument type. The dynamic binding only happens when calling a function directly through an object. So I thought I could do this, but it does seem like a roundabout way:
struct NumberObject : DataType
{
double data = 7;
void operator=(DataType& rhs) override
{
std::cout << "Copying from DataType\n";
rhs.assignTo(*this);
}
void operator=(NumberString& rhs) override
{
std::cout << "Copying from NumberString\n";
}
};
struct NumberString : DataType
{
std::string data = "7";
void assignTo(NumberObject& lhs) override
{// Provided the base class had this
lhs.data = atof(this->data.c_str());
}
void operator=(NumberString& rhs) override{};
};
This seems like a hack, and seeing as though the language is capable of dynamic binding, isn't this something it could do easily? Is it a weakness of the language? Or does nobody care about it?
With double dispatching, it would be:
struct NumberObject;
struct NumberString;
struct DataType
{
virtual ~DataType() = default;
virtual void operator=(const DataType&) = 0;
virtual void assignTo(NumberObject&) const = 0;
virtual void assignTo(NumberString&) const = 0;
};
struct NumberObject : DataType
{
double data = 7;
void operator=(const DataType& rhs) override { rhs.assignTo(*this); }
void assignTo(NumberObject& rhs) const override
{
std::cout << "Copying from NumberObject\n";
}
void assignTo(NumberString& rhs) const override
{
std::cout << "Copying from NumberObject\n";
}
};
struct NumberString : DataType
{
std::string data = "7";
void operator=(const DataType& rhs) override { rhs.assignTo(*this); }
void assignTo(NumberObject& rhs) const override
{
std::cout << "Copying from NumberString\n";
}
void assignTo(NumberString& rhs) const override
{
std::cout << "Copying from NumberString\n";
}
};
Demo
Given an abstract base class Object and 2 derived classes, Person and Gathering. Where Gathering is a data structure which stores pointers to Person or other Gathering pointers inside of an array.
I would like to override the output operator so it prints any type of Object. But I do not know how to override the operator properly so when it receives type Object it knows how to deal with it.
Here is a simple code that exemplifies what I'm trying to achieve:
#import <iostream>
#import <cstring>
class Object {
public:
virtual ~Object(){};
};
class Person : public Object {
private:
char * m_name;
public:
Person(char * input) {
m_name = new char[strlen(input)];
strncpy(m_name, input, strlen(input));
}
char* getName() const{
return m_name;
}
friend std::ostream& operator<<(std::ostream& os, const Person* p) {
os << p->getName();
return os;
}
};
class Gathering : public Object {
private:
int m_size;
Object* m_buffer;
public:
Gathering() : m_size(10)
{}
friend std::ostream& operator<<(std::ostream& os, const Gathering* v) {
for (int i=0; i<v->getSize(); i++) {
//Trying to send Object to outputstream..
os << "[" << v->getBuffer()[i] << "]";
}
}
int getSize() const {
return m_size;
}
Object* getBuffer() const {
return m_buffer;
}
};
I am very aware of what the problem is, how do Ideal with this? Any references or pointers are very appreciated.
You can add a virtual member function in Object, call it in your operator overload and implement it correctly for children classes.
class Object {
public:
virtual ~Object(){}
virtual void print(std::ostream&){}
friend std::ostream& operator<<(std::ostream& os, const Object& obj) {
obj.print(os);
return os;
}
};
class Gathering : public Object {
private:
int m_size;
Object* m_buffer;
public:
Gathering() : m_size(10)
{}
virtual void print(std::ostream& os) {
for (int i=0; i<v->getSize(); i++) {
os << "[";
m_buffer[i].print(os);
os << "]";
}
}
int getSize() const {
return m_size;
}
Object* getBuffer() const {
return m_buffer;
}
};
Also not related, but put a virtual destructor in your children classes, use a vector instead of your Object pointer m_buffer or at least initialise it in your constructor, but i guess you have school restrictions or something :)