-1

Sorry for asking I believe a repeated question, but I can't figure out the problem here, maybe my understanding of this is not correct.

Setup

    class Job {
    public:
        virtual bool createJob() {
            return false;
        }
    };

    class Limit {
    public:
        virtual bool updateLimit() {
            return false;
        }
    };

    class Base : public Job, public Limit {
    private:
        std::string cx;
    public:
        virtual ~Base() {

        }
    };

    class DerivedJob : public Base {
    protected:
        std::string ax;
        std::string bx;
        std::vector<std::string> vx;
    public:
        bool createJob() override {
            return true;
        }
    };

    class DerivedLimit : public Base {
    protected:
        std::string ax;
        std::string bx;
        std::vector<std::string> vx;
    public:
        bool updateLimit() override {
            return true;
        }
    };

Should work?

    Base parent;

    auto stat = static_cast<DerivedJob&>(base);
    std::cout << "JOB: " << std::to_string(stat.createJob());
    std::cout << "LIMIT: " << std::to_string(stat.updateLimit());

    auto dyn = dynamic_cast<DerivedJob&>(base);
    std::cout << "JOB: " << std::to_string(dyn.createJob());
    std::cout << "LIMIT: " << std::to_string(dyn.updateLimit());

    auto stat1 = static_cast<DerivedLimit&>(base);
    std::cout << "JOB: " << std::to_string(stat1.createJob());
    std::cout << "LIMIT: " << std::to_string(stat1.updateLimit());

    auto dyn1 = dynamic_cast<DerivedLimit&>(base);
    std::cout << "JOB: " << std::to_string(dyn1.createJob());
    std::cout << "LIMIT: " << std::to_string(dyn1.updateLimit());

I Tried many examples and read many posts, but when I add string and other garbage to my derived classes, it stops working, maybe my fundamental understanding of this is incorrect?

Don't be to harsh, I am only a junior in c++ :D

Thank you for your time explaining these things to me.

Additional question

How does dynamic casting work?

Example

class Device {
private:
  Base _base;
public:
  Device(int x) {
    if (x == 5) {
       DerivedJob object;
       object.test = 5;
       object.test2 = 1;
       _base = object;
    } else if (x == 7) {
       DerivedLimit object;
       object.test = 5;
       object.test2 = 1;
       _base = object;
    } ...
  }
}



Modestas
  • 41
  • 1
  • 5
  • The two `static_cast` invoke undefined behavior, and the two `dynamic_cast` return `nullptr` and cause UB on the next lines. For an object to be a `DerivedJob` or `DerivedLimit` it must be created as a `DerivedJob` or `DerivedLimit`. You cannot create it as `Base parent;` then hope it magically becomes another derived type just by casting. – dxiv Mar 08 '21 at 07:30
  • it was a typo, I understand that they generate undefined behavior, can someone explain me why this does not work? How it should be in my case done? – Modestas Mar 08 '21 at 07:33
  • Above comment answered my question, but I have additional one. How can I simulate dynamic `derived` object `assigment`? for example: `if (smth == 1) { DerivedJob job; job.test = 1; job.test2 = 5; base = job; } else if (smth == 2) { DerivedLimit limit; limit.test = 1; limit.test2 = 5; base = limit; }` is this the only way to do this? – Modestas Mar 08 '21 at 07:39
  • Not sure what you mean by "*dynamic derived object assignment*". You should [edit](https://stackoverflow.com/posts/66525703/edit) the question and add any clarifications there, not as comments. Also, correction to my first comment: since you `dynamic_cast` to references, not pointers, the failure will result in a [`std::bad_cast`](https://en.cppreference.com/w/cpp/types/bad_cast) exception (not a `nullptr`) though the result is still the same i.e. a fatal runtime error. – dxiv Mar 08 '21 at 07:41
  • @Modestas, that won't even compile, because you can't copy a derived class to a base class. Furthermore, references cannot be assigned after their initial creation (they are an alias to the variable being assigned) and if you used a pointer (&limit), the object would still be deallocated at the end of the block and you'd segfault. What you want is `Base *basePtr; if (smth == 1) { basePtr = new DerivedJob() }` – Refugnic Eternium Mar 08 '21 at 07:44

1 Answers1

0

As dxiv has already stated in his comment, static/dynamic_cast will only work, if the object in question actually IS that class.

They are hints to the compiler that a pointer/reference to a variable is of a certain class, but they do not convert the base class to the subclass.

So this would work:

Base *basePtr = new DerivedJob();
std::cout << "JOB: " << std::to_string(basePtr->createJob()); //True, because DerivedJob overrides the virtual method.
std::cout << "LIMIT: " << std::to_string(basePtr->updateLimit()); //False, because DerivedJob inherits the method from its parent.
static_cast<DerivedJob *>(basePtr)->doSomethingSpecificForDerivedJob();
auto dyn1 = dynamic_cast<DerivedLimit *>(basePtr); //NULL, because basePtr is NOT a DerivedLimit.
auto dyn2 = dynamic_cast<Limit *>(basePtr); //Address to basePtr, because basePtr inherits from Limit.

So, to summarize, use static_cast to invoke methods specific/only existing for the derived class and only when you're absolutely sure that it either IS that class or inherits from that class.

Use dynamic_cast, if you're not 100 % sure and want to check beforehand.

To pick up on the comment, what you're (probably) after is something like this:

Base *base = nullptr;
if (smth == 1)
{
    DerivedJob *job = new DerivedJob(); //Dynamic allocation instead of static!
    job->test1 = 1; //Requires 'test1' to be public
    base = job; //Passes the pointer to the variable pointing to the base class.
}

delete base;        Cleans up the instantiated DerivedJob instance.
Refugnic Eternium
  • 4,089
  • 1
  • 15
  • 24