improved compilation

I'm trying to come with a way to allow the default construction of a class, but throw an error if the user tries to access a member of the class without fully initializing the object.

Similar to how Java would allow:
1
2
3
City my_city;
my_city = new City("New York");
my_city.do_something();


but throw an error if you tried:
1
2
City my_city;
my_city.do_something();


I want my c++ code to allow:
1
2
3
City my_city;
my_city = City("New York");
print(my_city.name());


but throw an error if I try:
1
2
City my_city;
print(my_city.name());


I'm wondering if maybe there's something in a boost library I could extend my class off of that might accomplish something like this, but I'm not sure what I'm looking for.
The only other way I've come up with so far would be to put a check inside each method on the City object to make sure I've set some kind of init flag on the object before it executes.
Writing
City my_city;
implicitly calls the default constructor anyway. Besides, if no default constructor exists, this does result in an error.

EDIT: Thus, there is no need to worry about my_city not being fully constructed. Assuming the default constructor does its job, it must be.
Last edited on
closed account (3hM2Nwbp)
You can try making the default constructor inaccessible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <string>
class City
{
  private:
   City();
  public:
   City(const std::string& name);
};

int main()
{
  City city; // <-- Will Not Compile
  City city2("Hello City"); // Will Compile
  return 0;
}


It's not exactly what you're looking for, but the only other alternative that I can think of is to wrap your city objects in a smart pointer. There's a pitfall to this as well, because a NullPointerException won't be thrown in C++ if you try to deference a null pointer - an unrecoverable hardware exception will occur.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <memory>
class City
{
  public:
   void doSomething(void) { }
};

int main()
{
  std::scoped_ptr<City> my_city;
  my_city->doSomething(); // <-- Either a debug assertion will trigger, or a hardware exception.
  my_city.reset(new City);
  my_city->doSomething(); // <-- Works as expected.
  return 0;
}
Last edited on
Xander - The problem is though if default constructions succeeds that doesn't mean any of the values of the members are set to anything correct. They're all set to default values like "" or 0.

I don't want someone to be able to default construct, but then try and access a member that doesn't have a valid value in it. I want to insure that some kind of full constructor or init method has been called before allowing access to other methods.

Luc - So if I did....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class City
{
   private:
     City();
     std::string name_;
   public:
     City(const std::string& name):name_(name);
     const std::string& name()const{
           return name_;
     }
}

int main()
{
     City city1;
     city1 = City("Boston");
     print(city1.name());  <- would succeed;

     //but
     City city1;
     print(city1.name());  <- would fail?
}
closed account (3hM2Nwbp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class City
{
   private:
     City();
     std::string name_;
   public:
     City(const std::string& name):name_(name);
     const std::string& name()const{
           return name_;
     }
}

int main()
{
     City city1; // <-- This line would give you a compile-time error. (default constructor inaccessible)
     city1 = City("Boston");
     print(city1.name());  //<- would succeed;

     //but
     City city1; // <-- Another compile-time error (default constructor inaccessible)
     print(city1.name());  //<- This wouldn't be able to happen, as it couldn't compile.
}
Last edited on
ic. Thanks guys.
OR, to respond to his original question:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyClass
{
  int data;
  bool init;
public:
  MyClass(){ init = false; }
  void InitClass(int dat){ data = dat; init = true; }
  int& GetData()
  {
    if(init)
    {
      return(data);
    }
    throw(/*some exception*/);
  }
};
And then:
1
2
3
4
5
6
7
int main()
{
  MyClass obj;
  cout << obj.GetData() << endl; //Throws an exception
  obj.InitClass(1337); //Sets up the class correctly
  cout << obj.GetData() << endl; //Does not throw an exception
}
Last edited on
Maybe a pointer? Your java code
1
2
3
City my_city;
my_city = new City("New York");
my_city.do_something();


would in c++ be
1
2
3
City* my_city;
my_city = new City("New York");
my_city->do_something();
@Luc Lieber
Is there any need to make the default constructor private? Why not just exclude the default constructor. Then, provided another constructor is included, no default one will be generated at all, as far as I am aware.
@Xander314
What you say is true.

@ischuldt
Reading this thread carefully in its entirety, it seems to me that L B's solution is the closest to what your OP was asking for. However, I find the implications in this thread somewhat troubling.

In your OP, the Java code

1
2
City my_city;
my_city.do_something();


dies for a reason that's different than the problem raised by your OP. It's failing because my_city doesn't point to anything (equivalent of a NULL ptr in C++).

Exactly the same code in C++ actually constructs an instance (an automatic variable that's cleaned up when it falls out of scope), even though instance may not be initialized with an actual valid value of a real city.

Are you trying to port Java code to C++? I am getting the uneasy feeling that you may be trying to make C++ behave like Java or you are using Java thinking in C++...

C++ != Java
yeah L B's solution is the one I came up with originally. I was hoping for something better than wouldn't require an if check to be added to every getter, setter and method in the class, but...

c++ may not be java, but there are some features of java that are really nice.
Actually, what naraku9333 posted is also correct.

So for Java error-throwing that looks like this:
1
2
City my_city;
my_city.do_something();


the C++ should actually be this:
1
2
City *my_city = NULL;
my_city->do_something();


which would segfault during runtime.

If you are still intent on making your C++ code safer, you can also look into assert() (C version) or static_assert() (Boost, C++0x) for a more concise way of checking for a valid object (google for examples).
Last edited on
here's a crazy idea. What about overloading the . operator or -> operator to automatically do the check for you so you don't have to add it all the time.

so something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class City{
private:
     bool init_flag_;
     string name_;
public:
    City(){init_flag_ = false}
    void init(const string& name){
         name_=name;    
         init_flag_ = true;
    }
    const string get_name() const;    

    City *operator->(){
          if(init_flag){
              return this;
          }
          throw some_exception();
    }

}


int main()
{
     City my_city;
     my_city.init("New York");
     my_city->get_name() // doesn't throw an exception.

     City my_city;
     my_city->get_name() // throws an exception

}
Last edited on
Topic archived. No new replies allowed.