15

Specifically asking about the default constructor

Given that the constructor initializes all the data for an object, if I create a class that can't be used without proper initialization, is it not the case that the default constructor is useless? Consider:

// A class for handling lines in a CSV file
class CSV_Entry {
private:
    unsigned num_entries;
    std::string string_version;
    std::vector<std::string> vector_version;
    ...etc
public:
    CSV_Entry();
    CSV_Entry(const std::string& src_line);

    // returns a vector copy of the original entry
    std::vector<std::string> get_vector_snapshot();
}

int main( void ) {
    ...etc

    CSV_Entry example = CSV_Entry();
    std::vector<std::string> current_entry = example.get_vector_snapshot();

    ...etc
}

That variable current_entry is essentially useless no? If someone tries to process it later on, they would likely get errors; then they'd create code to handle such errors...

To mitigate such additional, unnecessary code: why not make the default constructor unusable? Like so,

...etc

CSV_Entry() {
    throw Verbose_Exception( "CSV_Entry: do not use the default constructor" );
}

...etc

PS: on a side note, if it is fine to just make the default constructor unusable, is it fine to put that throw in the header, since no other implementation details are revealed anyway?

Alexander
  • 378
  • 1
  • 5
  • 14
user2738698
  • 957
  • 1
  • 8
  • 20

1 Answers1

36

Yes, it's fine (actually, it's good) to make the default constructor unusable if there's no sensible way to initialize the object without any arguments. But don't "disable" it by throwing an exception. Make it private instead. Ideally your interface won't contain any methods or constructors people "aren't supposed to" call.

Doval
  • 15,347
  • 3
  • 43
  • 58
  • 1
    So, by making it private, the user trying to use the default constructor will get an error at compile time? – user2738698 Mar 26 '14 at 18:07
  • @user2738698 Correct. – Doval Mar 26 '14 at 18:10
  • 8
    If you can use C++11, then explicitly mark it as deleted: `CSV_Entry() = delete;`. – bstamour Mar 26 '14 at 18:29
  • 13
    Actually, isn't it even easier than this? If any non-default constructors are defined, the compiler will not implicitly create a default constructor. This class has a non-default constructor defined (which I'd recommend be `explicit`, BTW). So if you just don't define it, it won't exist. – Fred Larson Mar 26 '14 at 20:39
  • @FredLarson Also a good point. – Doval Mar 26 '14 at 20:40
  • 7
    @FredLarson Explicitly deleting it expresses the intent of deleting it so no one thinks it was a mistake. – Darkhogg Mar 26 '14 at 20:55
  • does c++98 have explicit? – user2738698 Mar 26 '14 at 21:11
  • @user2738698: Yes, it has `explicit`. – Fred Larson Mar 26 '14 at 23:17
  • If a particular constructor should be usable *only* for creating base-class instances, but not for creating derived-class instances, would there be any way to enforce that other than by throwing an exception if `this.GetType() != typeof(myType)`? – supercat Jul 09 '14 at 23:06
  • @supercat Maybe not, but under what circumstance would you want to do that? – Doval Jul 10 '14 at 11:20
  • @Doval: A class whose purpose is to attach itself to another object and receive events therefrom should perform such attachment only after all other instance construction is complete. If one is constructing a base-class instance, the base-class constructor itself needs to perform such attachment because nothing else is going to do it. If one is constructing a derived-class instance, however, the base-class constructor should not perform such attachment but should leave the job to the derived-class constructor. – supercat Jul 10 '14 at 15:35
  • @Doval: I wish that instead of `Finalize()`, .NET had included a protected `ManageLifetime` method with a parameter indicating various kinds of things that needed to happen, and would have called such a method with a `ConstructionComplete` action between the call the outermost constructor and the return to calling code, and with a `ConstructionFailed` action if a constructor failed due to an exception. That would have allowed such event-attachment scenarios to be handled much more cleanly. No such thing exists, though, so using separate constructors is the best alternative I can see. – supercat Jul 10 '14 at 15:38
  • @Doval: Using separate constructors, one could simply say that as part of the inheritance contract, that the outermost constructor is required to call a protected sealed `ConstructionComplete` method, which would then finish construction within a `try/catch` block that would ensure proper cleanup if anything went wrong. – supercat Jul 10 '14 at 15:41