Jump to content

  • Log In with Google      Sign In   
  • Create Account

#ActualTrienco

Posted 05 February 2013 - 11:27 PM

Since const correctness still comes across as something that's "nice but not mandatory", let's look at things that will fail if you don't write const correct code.

 

First, the one thing that can really get me mad: people writing interfaces/APIs using char* instead of const char*.

void parseString(char* str) { ... }
 
std::string text;
parseString(text.c_str()); //Fail, as c_str() returns const char*

 

 

While these have a simple workaround, it can still be annoying

void analyseObject(MyClass& obj) { ... }
 
analyseObject( MyClass(a,b,c) ); //Fail, temporary variables are r-values and const

 

struct MyClass {
   void print() {...}
}
 
MyClass(a,b,c).print(); //Fail, temporary = const

 

 

To clarify the observable effects of ignoring the Rule of Three, because unlike missing const correctness, this won't conveniently fail to compile in the first place:

 

struct MyClass
{
    int* data;
    MyClass() : data(new int) {}
    ~MyClass() { delete data; }
};
 
 
vector<MyClass> v;
v.push_back(MyClass()); //Fuse is lit
cout << v.front().data; //Fail
v.clear(); //Fail
 
void function(MyClass a) { ... }
 

 

Let's ignore compilers optimizing out the temp variable or using move semantics, since you can't rely on it.

 

First example creates a temporary instance, then creates a copy in the vector (which just stupidly copies the pointer). The temporary goes out of scope and deletes data. The next line accesses data that was just deleted. You will see either garbage (kind of helpful), crash with an access violation (very helpful) or it will just seem to "work fine" (dangerous), as the memory will most likely not have been overwritten yet.

 

Then we clear the vector and delete the instance in it. Now data is deleted a second time. If you are lucky, your application will crash.

 

 

The second example looks harmless, but does pretty much the same. To call the function, you create a copy. When the function returns, this copy is destroyed and deletes your data. The original object is now in an invalid state with data pointing to deleted data. Any access will hopefully crash and when the original object goes out of scope, the memory is attempted to be deleted a second time.

 

 

Why is crashing the best case scenario? Because it is hard to miss and clearly tells you there is a bug. The same bug could result in working "just fine" most of the time, but returning strange values on every odd Thursday during full moon. This can cost you days or weeks just trying to reproduce the bug and tracking it down.


#1Trienco

Posted 05 February 2013 - 11:26 PM

Since const correctness still comes across as something that's "nice but not mandatory", let's look at things that will fail if you don't write const correct code.

 

First, the one thing that can really get me mad: people writing interfaces/APIs using char* instead of const char*.

void parseString(char* str) { ... }
 
std::string text;
parseString(text.c_str()); //Fail, as c_str() returns const char*

 

 

While these have a simple workaround, it can still be annoying

void analyseObject(MyClass& obj) { ... }
 
analyseObject( MyClass(a,b,c) ); //Fail, temporary variables are r-values and const

 

struct MyClass {
   void print() {...}
}
 
MyClass(a,b,c).print(); //Fail, temporary = const

 

 

To clarify the observable effects of ignoring the Rule of Three

 

struct MyClass
{
    int* data;
    MyClass() : data(new int) {}
    ~MyClass() { delete data; }
};
 
 
vector<MyClass> v;
v.push_back(MyClass()); //Fuse is lit
cout << v.front().data; //Fail
v.clear(); //Fail
 
void function(MyClass a) { ... }
 

 

Let's ignore compilers optimizing out the temp variable or using move semantics, since you can't rely on it.

 

First example creates a temporary instance, then creates a copy in the vector (which just stupidly copies the pointer). The temporary goes out of scope and deletes data. The next line accesses data that was just deleted. You will see either garbage (kind of helpful), crash with an access violation (very helpful) or it will just seem to "work fine" (dangerous), as the memory will most likely not have been overwritten yet.

 

Then we clear the vector and delete the instance in it. Now data is deleted a second time. If you are lucky, your application will crash.

 

 

The second example looks harmless, but does pretty much the same. To call the function, you create a copy. When the function returns, this copy is destroyed and deletes your data. The original object is now in an invalid state with data pointing to deleted data. Any access will hopefully crash and when the original object goes out of scope, the memory is attempted to be deleted a second time.

 

 

Why is crashing the best case scenario? Because it is hard to miss and clearly tells you there is a bug. The same bug could result in working "just fine" most of the time, but returning strange values on every odd Thursday during full moon. This can cost you days or weeks just trying to reproduce the bug and tracking it down.


PARTNERS