One annoyance aspect as a programmer is having to to know the sublets of the language. Many features are great on paper, however, you run into trouble immediately if you do not understand the possible issue associated.
Many C++ programmers are now familiar with keyboard const. They want to apply the keyword anywhere if they see fit. They argue that const keyword adds a lot more constraints that help discover bugs by compilers, and itself also help compiler to optimize the code for better performance.
Let’s start with a simple example:
class IRule
{
public:
virtual const char* getName() const = 0;
};
The above is an abstract class, which defines the methods that all derived classes must implement. The method returns a const pointer, which means that the caller can’t change the content that the pointer points to. The const at the end indicates that the implement should not change the object state, a.k.a. const this pointer.
All went well and you wrote a dozen derived classes that inherits IRule. A couple of months later you have to implement another one, and this time, the situation is a little bit different. You have to obtain the name from a remote server, and for performance reason, you can only do so at the first time it called. You write this:
class myRemoteRule : public IRule
{
public:
myRemoteRule(const char* serverA);
virtual const char* Name(void) const;
private:
std::string strName;
std::string serverAddr;
};
const char* myRemoteRule::Name(void) const
{
if (strName.empty()) {
strName = getNameFromRemote(serverAddr);
}
return strName.c_str();
}
Cool. How the above code does not compile. The compiler complains that you can’t declare the method as const as the data member strName, is modified in it. Now bad. Either you have to find a workaround, or just go change all declaration of this method, which can take a while. Luckily, C++ allows us to cast the const off using const_cast, so the following code compiles:
const char* myRemoteRule::Name(void) const
{
if (strName.empty()) {
const_cast<myRemoteRule*>(this)->strName = getNameFromRemote(serverAddr);
}
return strName.c_str();
}
Let’s look at the real case – the example above is just too trivial. In reality, a class may have several dozen methods and data members. It is difficult to predict whether we may run into such situation when we wrote the interface prototype. We may well end up with writing a lot of const_cast in the place, further glutting the code.
The morale of the story is that in real world programming is not easy. Simply applying const on a seemingly const construct may be an over constraint and hurts the coding.
Type size_t is well know to C++ programmers. It is an alias for whichever unsigned integer type capable of representing the size of the largest possible object in the target environment. Depending on the target, size_t might be unsigned, unsigned long, or unsigned long long.
There is another type frequently used by C programmers - ssize_t. From its name, it represents a signed size_t. Unfortunately, it is not part of C standard. C standard library provides another types, ptrdiff_t for this purpose - although on the first glance, the two looks quite different.
ssize_t is not defined in VC++ (at least in VC 8.0). It is available in GCC. If a library is written using this type, it will compile OK under gcc, but not under msc. For this reason, many library has the following code (or similar):
#if !defined(ssize_t) #define ssize_t long #endif
Now comes the problem - if you have two such libraries defining ssize_t, you may run into problem when you include header files from both libraries. Worse, some libraries use typedef to define this type, and two typedefs (or one define) conflict each other.
The suggestion here is not to use this ssize_t as it is non-standard. Use ptrdiff_t instead.