6

I'm a Java developer trying to pick up C++. Is it okay to use a setter inside a constructor in order to reuse the sanity checks the setter provides?

For example:

#include <stdexcept>
using namespace std;

class Test {
    private:
        int foo;
        void setFoo(int foo) {
            if (foo < 42) {
                throw invalid_argument{"Foo < 42."};
            }

            this->foo = foo;
        }

    public:
        Test(int foo) {
            setFoo(foo);
        };
};
6
  • It is valid. Note that unsigned types exist to get rid of the test here. Commented Jul 13, 2016 at 8:48
  • If you don't want users of the class to not pass negative numbers, why not use unsigned instead? Then the compiler will handle the check at the time of compilation for you, instead of you needing a runtime check. Commented Jul 13, 2016 at 8:48
  • 2
    The unsigned stuff is not undisputed; see e.g. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50 (Panel in which several prominent members of the committee argue against using unsigned integers for stuff other than bit-fiddling.) Commented Jul 13, 2016 at 8:49
  • 1
    Off-top: is it normal to throw exceptions in constructor? Commented Jul 13, 2016 at 8:54
  • 1
    @ilotXXI Yes. Exceptions are a way to guarantee a class invariant if some constructor input wouldn't satisfy it. Commented Jul 13, 2016 at 8:57

6 Answers 6

7

Yes, it is recommended to do this, basically for the reason you already mentioned.

On the other hand you should ask yourself if you need the setter at all and not directly implement the checks inside the constructor. The reason I am writing this is that setters in general result in mutable state which has many disadvantages as opposed to immutable classes. However sometimes they are required.

Another recommendation: If your class variable is an object and you can modify the constructor of this object, you could put the check into the constructor of this object:

class MyFoo {
public:
    MyFoo(int value) {
        if (value < 42) {
            throw invalid_argument{"Foo < 42."};
        }
        v = value;
    }
private:
    int v;
}

This will enable you to use an initialization list in the constructor of your Test class:

Test(int foo) : foo(foo) {}

However, now the check is a property of the class of the variable and no longer one of the owning class.

Sign up to request clarification or add additional context in comments.

Comments

3

Yes you can. It's fine as long as your setters are not virtual, because it's inheritance hierarchy in calling right functions as the "this" ptr is not ready yet.

Here is Herb Sutter GOTW on this matter: http://www.gotw.ca/gotw/066.htm

5 Comments

@user2079303 I agree it's fine as long as dev is aware which function will be called in such situation. I've never thought about errors coming from calling non-virtual functions calling virtual onces. Good point.
@user2079303: "What you absolutely musn't do is call a member function that in turn calls a virtual member" Why not? How's that different from what you just said is safe?
@user2079303: 'Fraid so. Non-constructors do dynamic dispatch, and constructors do dynamic dispatch. The result is deterministic. It might not be what you expected if you're in the middle of constructing a more-derived object, but it's "safe" and assuredly has the same effect in both cases.
Modified example showing that bar()'s virtual dispatch will "know about" the most derived class after construction is complete: coliru.stacked-crooked.com/a/a21681a2e2c87e78
@LightnessRacesinOrbit nice, thanks for setting my misinformation straight. I shall remove their mention so that other's won't be corrupted.
1

Yes, that's fine as long as it makes sense to have a setter for a particular member variable (have some logic that can't be checked by assignment only for example) . In this example, setFoo could've just taken an unsigned int and the caller would know not to pass negative values. Which in turn could eliminate the check and thus the need for a setter. For more elaborate checks, a setter and usage of that setter in the constructor is just fine.

Comments

1

Short answer: Yes. In fact, your example works.

Long answer: But it is not a good practice. Al least, you have to take care.

In general, a set function works with a constructed object. It is supposed that the invariant of the class holds. The functions in a class are implemented considering the invariant is true.

If you want other functions to be used in a constructor, you would have to write some code. For example, to create an empty object.

For example, if in your class you change setFoo in the future (let's say setFoo changes the member foo only it is larger) you example stop working.

Comments

0

This is okay.

The only situation you cannot call member function is when the base classes are not constructed yet.

can member functions be used to initialize member variables in an initialization list?

Comments

0

I know this doesn't fit your situation. Its just for the sake of completeness:

When you are simply settings member values (without checks like yours in setFoo) it is recommended to use initialization lists in the constructor. This prevents members being "initialized" 2 times: 1. with their default value, 2. with the value that you passed into the constructor.

class Test {
private:
    int foo_;

public:
    Test(int foo)
      : foo_(foo)
    { };
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.