0

Consider the following example where the construction of Derived class takes a pointer on its constructor's initializer list. Of course I want to check if this pointer is valid and throw an exception otherwise.

My attempt prevents the program to crash but Base part is still constructed before I can throw the exception.

Is there a way I can prevent Base class constructor being called in this case ?

#include <stdlib.h>
#include <iostream>

class Base
{
public:
    Base(int val) : val_b(val)
    {
        std::cout << "Base::CTOR" << std::endl;
    }
    ~Base() { }

    int val_b;
};

class Derived : public Base
{
public:
    Derived(int *, int);
    ~Derived() { }

    int val_d;

    void print(void)
    {
        std::cout << "Base:\t" << val_b << std::endl;
        std::cout << "Derived:" << val_d << std::endl;
    }

};

Derived::Derived(int *val1, int val2) : Base(val1 ? *val1 : -1), val_d(val2) 
{
    if (!val1)
    {
        throw std::invalid_argument("bad pointer");
    }
}

int main()
{
    int *a = NULL;  
    int b = 43;

    try 
    {
        Derived *d = new Derived(a, b);
        d->print();
    }
    catch (std::exception &e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
    }

    return 0;
}
Odysseus
  • 1,213
  • 4
  • 12
  • 3
    This sounds like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Can you explain why you want this ? – Sander De Dycker Mar 26 '20 at 11:00
  • And for reference : [Order of calling constructors/destructors in inheritance](https://stackoverflow.com/questions/7539282/order-of-calling-constructors-destructors-in-inheritance) – Sander De Dycker Mar 26 '20 at 11:02
  • you cat throw in Base ctor, if it gets invalid value (-1 in your code) – olpchk Mar 26 '20 at 11:20
  • If it's invalid to call your `Derived` constructor with a null pointer, why did you make the parameter type a pointer ? Why not pass by value or reference, and avoid the whole problem ? – Sander De Dycker Mar 26 '20 at 11:39
  • @SanderDeDycker Actually I pass an object pointer to `Derived` class ctor and only need one member for `Base` initialization - I just simplified that by using `int*` – Odysseus Mar 26 '20 at 11:59
  • @Odysseus : then why not pass that one member directly ? – Sander De Dycker Mar 26 '20 at 12:35
  • Reviewing the answers/comments the answer seems to be 'no' (exept throwing an exception from a lambda function before) - so I might think about reorganizing classes - Is it bad practice at all to use object pointers in initializer list? – Odysseus Mar 26 '20 at 12:38
  • @SanderDeDycker This would indeed be cleaner since I wont't need to check pointer but I still cant prevent base class from being constructed – Odysseus Mar 26 '20 at 12:44
  • @Odysseus : two things : (a) with that approach there would be no more exception possible, so you avoided the problem entirely, and (b) you still haven't explained *why* you want to avoid constructing the base class. – Sander De Dycker Mar 26 '20 at 12:46
  • @SanderDeDycker (a) Only if I pass all needed members of the object pointer directly which would be a bunch of members for the derived class, thats why I used a pointer (b) I just want to prevent unnecessary code to be run /memory allocated if it is clear right away that instantiation will fail – Odysseus Mar 26 '20 at 12:54
  • fwiw, it seems like you already know if it can be constructed before you even call `Derived` constructor. There is nothing bad about throwing from a constructor, but if you want to prevent calling the constructor, why not first check if `val1` is not `nullptr` – 463035818_is_not_an_ai Mar 26 '20 at 13:04
  • @idclev463035818 That would also be legit attempt but I also don't like functions which do not check pointer parameters. You have to make sure to check for that everytime the function/ctor is used – Odysseus Mar 26 '20 at 13:15

3 Answers3

4

You might call a function/lambda before calling Base constructor:

Derived::Derived(int *val1, int val2) :
    Base([&](){
        if (!val1) {
            throw std::invalid_argument("bad pointer");
        }
        return *val1;
    }()),
    val_d(val2) 
{
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Maybe I misunderstand your question, but consider this simplified example:

#include <iostream>

struct Base {
    ~Base() { std::cout <<"destructor";}
};
struct Foo : Base {
    Foo() : Base() {
        throw 1;
    }
};
int main()
{
    try {
        Foo f;
    } catch(...){}
}

Output is:

destructor

My attempt prevents the program to crash but Base part is still constructed before I can throw the exception.

That isn't a problem. As always with exceptions, stack is unwinded and the Base part of Foo is properly destroyed. I see nothing wrong in your code (in the sense of seriously broken, though design is debatable). If construction fails and you throw an exception in the body of the constructor, cleaning up what already has been constructed is the best you can do.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • I was not concerned that base class part is not properly released - I just wanted to avoid its constructor being called if invalid pointer occurs in initializer list – Odysseus Mar 26 '20 at 12:18
  • @Odysseus yes, but it is not clear why you want to do that. In such case there are two ways to approach the question: 1) do some guessing on what was the original intention and propose a different solution 2) just take the question literally and answer it. You basically have answers for both, I chose 1) and seems like my guessing was not quite correct – 463035818_is_not_an_ai Mar 26 '20 at 12:20
  • Well the actual question was if it is possible to prevent base class ctor being called when there is a invalid pointer in derived class initializer list. And the answer to this seems to be no - The only way so far seems to work around with a lambda function. But knowing this I can now think about to restructure my classes – Odysseus Mar 26 '20 at 12:34
  • 1
    why do you say the answer is no? Jarods answer shows how you can prevent the `Base` constructor from getting called. Though I do agree that you should rather fix the design – 463035818_is_not_an_ai Mar 26 '20 at 12:36
0

I don't get why you want it but anyway have you tried failing in Base ctor rather than Derived ctor?

class Base
{
public:
    Base(int *val)
    {
        if (!val)
        {
            throw std::invalid_argument("bad pointer");
        } 
        val_b = val;
        std::cout << "Base::CTOR" << std::endl;
    }
    ~Base() { }

    int val_b;
};
theWiseBro
  • 1,439
  • 12
  • 11
  • For sure I can restructure my classes but I wanted to know if there is some elegant way to avoid that – Odysseus Mar 26 '20 at 12:46