-1

I have a class that is called Object, this class's header is:

class DLL_SPEC Object {
public:
    Object();
    virtual ~Object();

    virtual std::string getString() const;
    virtual void setString(std::string value);

    virtual int getInt() const;
    virtual void setInt(int value);

    virtual double getDouble() const;
    virtual void setDouble(double value);

    virtual bool isType(FieldType type) const;
};

And my child classes are as follows:

class DLL_SPEC IntObject : public Object {
public:
    IntObject() : value(0) {}
    IntObject(int v) : value(v) {}
    void setInt(int value) override { this->value = value; };
    int getInt() const override { return this->value; };
    bool isType(FieldType type) const override;
private:
    int value;
};

class DLL_SPEC DoubleObject : public Object {
public:
    DoubleObject() : value(0.0) {}
    DoubleObject(double v) : value(v) {}
    void setDouble(double value) override { this->value = value; };
    double getDouble() const override { return this->value; };
    bool isType(FieldType type) const override;

private:
    double value;
};
class DLL_SPEC StringObject : public Object {
public:
    StringObject() : value("") {}
    StringObject(std::string v) : value(v) {}
    void setString(std::string value) override { this->value = value; };
    std::string getString() const override { return value; };
    bool isType(FieldType type) const override;

private:
    std::string value;
};

Now, the problem is, I have an array of Objects and I want to get a string representation of a StringObject.

I call array[0].getString() and even though the object is of type StringObject, the method that gets called is the one is the base class, which I understand.

So, how would I go about implementing that whenever I call getString() on the base class it goes to the child one of the SAME object?

I've tried using this method:

std::string Object::getString() const
{
    return dynamic_cast<StringObject*>(this).getString();
}

but then I get an error stating I cannot cast away const or any type qualifier, which is fixed by deleting const modifier (which I MUST leave there as it's according to the task), but then I get another one stating that no suitable constructor exists. So how would I go about implementing this and getting this base class to use the one of the child one?

EDIT: Added a small example that goes into the getString method of Object class and not the StringObject class.

int findPersonId(std::string whereName)
{
    Db* db = Db::open("database");
    Table* people = db->openTable("table");
    auto iteratorTable = table->select();

    while (iteratorTable->moveNext())
    {
        for (size_t i = 0; i < table->getFieldCount(); i++)
        {
            if (table->getFields()[i]->getName() == "id")
            {    //this one beneath goes to the base class and not StringObject
                std::string foundRow = iteratorPeople->getRow()[i]->getString(); 
                if (foundRow == whereName)
                {
                    return iteratorTable->getRowId();
                }
            }
        }
    }
    return 0;
}

Note: The Table* is 2D array that consists of Object** (array that contains StringObject, IntObject, DoubleObject). The method .getRow() return the Object** array that consists of StringObject ...

The way I initiate the objects that go into the array is

Table* table= db->openOrCreateTable("table", 2, userFields); //this creates a 2d array
StringObject* name = new StringObject("Joseph");
IntObject* id = new IntObject(5);

Object** row = combineToRow(id, name);
table->insert(row); //insert an array into 2D array

The method combineToRow is just a simple convertor to Object**.

template<typename A, typename B>
Object** combineToRow(A a, B b) {
    return new Object * [2]{ a, b };
}
Antrophy
  • 29
  • 6
  • *"I have an array of Objects"* Sounds like the source of problems. Dynamic polymorphism can only be used with pointers/references. The whole point of `virtual` keyword is to dispatch the call to the actual class at runtime, so that you don't have to do it yourself. – Yksisarvinen Nov 17 '19 at 17:51
  • @Yksisarvinen the array is actually Object** and each index is either StringObject, IntObject or DoubleObject. I can do a workaround that whenever I want array[0] I call a dynamic_cast on that but then what would be the point of the getString method of the base class? – Antrophy Nov 17 '19 at 17:53
  • This doesn't solve the main problem, or the many design problems, but you can fix the `dynamic_cast` (which you don't really need anyway) simply by using `dynamic_cast(this)` – Spencer Nov 17 '19 at 17:55
  • @Spencer If I do that, then I get an error message stating that `no suitable constructor exists to convert from "const StringObject *" to "std::basic_string, std::allocator>"` – Antrophy Nov 17 '19 at 17:57
  • 2
    This kind of `dynamic_cast` does not accomplish anything. A particular `Object` is either a `StringObject`, or it's not. If it is a `StringObject`, then `StringObject::getString` overrides the one in `Object`. If it's not, a dynamic_cast in the `Object::getString` will, obviously, give you `NULL` because this `Object` is not a `StringObject`, and the shown code blows up. It is unclear what exactly you're trying to accomplish here. – Sam Varshavchik Nov 17 '19 at 17:58
  • 3
    `array[0].getString()` is not a valid syntax for array of pointers. And `dynamic_cast` is a bad smell, indication that something is wrong with the class hierarchy. You shouldn't have need to implement any such weird tricks in base class. Can you show us [mcve] of class with `virtual` methods that call base class methods instead of derived class methods (on a derived class object)? – Yksisarvinen Nov 17 '19 at 17:58
  • I think your real problem you're trying to communicate here is that you have an array of `Object`s. That's your problem. That won't work, C++ does not work this way. That's why you're struggling to cast here, looking for objects that no longer exist any more. They were sliced away. Look up "object slicing". – Sam Varshavchik Nov 17 '19 at 18:00
  • @Antrophy I didn't give you the entire expression, just the `dynamic_cast` part. If you insist on keeping the `dynamic_cast`, you'd want `return dynamic_cast(this)->getString()`. – Spencer Nov 17 '19 at 18:05
  • Your question should include how you instantiate your string objects. Also, in order to keep the example minimal, you could remove plenty of lines from your implementation. You'll get anwers much faster as the reader would be able to read your question in less than a minute. That said, you should read on polymorphism in C++ and the virtual function specifier. A good start could be here: https://en.cppreference.com/w/cpp/language/virtual – mfnx Nov 17 '19 at 18:07
  • @Yksisarvinen I've added an example of iterating through the Object** array where the method that gets called is the one of Object and not the StringObject. – Antrophy Nov 17 '19 at 18:17
  • @Spencer that's a great workaround though, thank you! But then I'd have no way of implementing setString or setDouble etc. in the Object base class. There must be other way to stop this maddness. I've added an example of how I handle initialising objects and putting it into an array into the post. – Antrophy Nov 17 '19 at 18:25

1 Answers1

1

You have not implemented a getString method for your IntObject, and since you didn't override it you are calling the base method. Once you implement it like this

class  IntObject : public Object { 
    ...
    virtual std::string getString() const { return std::to_string(value); };
    ...
};

then you can call it.

int main(){
    StringObject* name = new StringObject("Joseph");
    IntObject* id = new IntObject(5);

    Object** row =  combineToRow(id, name);    

    std::cout << row[0]->getString() << " " << row[1]->getString();
}
5 Joseph

See working version here

Thomas
  • 4,980
  • 2
  • 15
  • 30