0

Let's say I have this simple class with a const int member variable:

class MyClass{
    public:
        Myclass(int x, int y);
    private:
        const int importantNumber;
        int anotherNumber;
};

MyClass::MyClass(int x, int y) :importantNumber{x}
{
    this->anotherNumber = y;
}

Since int importantNumber is const, I can only set it during the creation of the object by the constructor (with a member initialization list, as seen above).

Now, the question: how could I possibly add validation for argument x given to the constructor before actually creating importantNumber with that value? Is it possible to create a static int MyClass::validation(int a) and use it on the member initialization list of the constructor like importantNumber{validation(x)}?

Even if it's possible, is there a better way to do it?

Silverman
  • 299
  • 1
  • 10
  • you can separate validation logic and processing logic. Something along lines "if validator allows parameters, create object and process it, otherwise handle invalid data". – Andrew Kashpur Nov 27 '18 at 11:48

3 Answers3

4

You just add it.

MyClass::MyClass(int x, int y) : importantNumber{validate(x)}
{
    this->anotherNumber = y;
}

The int validate(int original) function can now return something other than x or throw an exception or assert or ask the user for confirmation, whichever you deem appropriate.

If it is just a simple check and you don't want to write a validate function you can use a lambda:

MyClass::MyClass(int x, int y) :importantNumber{
    [](int number){
        assert(number > 0);
        return number;
    }(x)}
{
    this->anotherNumber = y;
}

Although this can get a bit convoluted if you overdo it.

nwp
  • 9,623
  • 5
  • 38
  • 68
1

You can use the ternary operator condition ? true : false in the constructor if you want to validate with a simple condition:

class MyClass{
    public:
        MyClass(int x, int y);
    private:
        const int importantNumber;
        int anotherNumber;
};

MyClass::MyClass(int x, int y) : importantNumber(x > 0 ? x : 0)
{
    this->anotherNumber = y;
}

However, be warned that things can quickly become difficult to read if you overdo it with this operator.

For something more complex, you could do something like this:

int validateIntegral(int x) const
{
    // Do validation on 'x'...

    return x;
}

class MyClass{
    public:
        MyClass(int x, int y);
    private:
        const int importantNumber;
        int anotherNumber;
};

MyClass::MyClass(int x, int y) : importantNumber(validateIntegral(x))
{
    this->anotherNumber = y;
}
not an alien
  • 651
  • 4
  • 13
0

Use factory function for creating a class instead of constructor.

class MyClass
{
public:
  static MyClass* create (int x, int y);

private:
  MyClass(int x, int y);

private:
  const int importantNumber;
  int anotherNumber;
};

MyClass* MyClass::create (int x, int y)
{
  return x > 0 ? new MyClass(x, y) : NULL;
}

When you need some advanced validation of parameters, factories have following advantages:

  • They avoid creation of object in memory if tests fail
  • They have more flexibility over checking parameters in initialization list
  • You can return NULL if you dont need exceptions nor you want to have some ".is_valid()" member function for your class.
grapes
  • 8,185
  • 1
  • 19
  • 31
  • Thank you! Was unsure about that, changed from ref to pointer now; anyway it doesn't affect the logic. – grapes Nov 27 '18 at 12:02
  • 1
    Now you have unclear ownership semantics and unnecessary dynamic allocation. Returning by value / throwing an exception would be better, wouldn't it? – Lightness Races in Orbit Nov 27 '18 at 12:33
  • In general if class is not designed to support moving operations (e.g. we use old C++ standard without support for movement), passing it by value is a bad idea. – grapes Nov 27 '18 at 12:36
  • Dont get me wrong, I dont argue for pointer vs value, it is completely out of topic. I agree about your arguments on ownership. It is possible to use smart pointers just in case, but I think, they would make my example over-complicated for this particular answer. – grapes Nov 27 '18 at 12:40
  • 1
    On the contrary, returning by value has always been the recommended approach. Compilers have been eliding unnecessary copies of returned values for many years, long before it became mandatory in C++17 and long before they became moveable in C++11. – Lightness Races in Orbit Nov 27 '18 at 12:55
  • 1
    @grapes: See [copy elision](https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization). – not an alien Nov 27 '18 at 13:10
  • 1
    Agree on that, thanks @notanalien. Leveraged today my c++. – grapes Nov 27 '18 at 13:32