Search code examples
c++overriding

C++ Function overload in the child


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

Solution

  • 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).