Home > Mobile >  C Function overload in the child
C Function overload in the child

Time:05-02

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

CodePudding user response:

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

CodePudding user response:

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