What are the most surprising elements of the C++ standard?
I've decided to get more acquainted with my favorite programming language, but only reading the standard is boring.
What are the most surprising, counter-intuitive, or just plain 开发者_运维技巧weird elements of C++? What has shocked you enough that you ran to your nearest compiler to check if it's really true?
I'll accept the first answer that I won't believe even after I've tested it. :)
I found it somewhat surprising that
class aclass
{
public:
int a;
};
some_function(aclass());
will have a
initialized to 0
in some_function
, while
aclass ac;
some_function(ac);
will leave it unitinitalized.
If you explicitly define the default constructor of aclass
:
class aclass
{
public:
aclass(): a() {}
int a;
};
then
aclass ac;
will also initialize a
to 0
.
Another answer I could add would be the throw()
qualifier. For example:
void dosomething() throw()
{
// ....
}
void doSomethingElse() throw(std::exception)
{
// ....
}
Intuitively, this seems like a contract with the compiler, indicating that this function is not allowed to throw any exceptions except for those listed. But in reality, this does nothing at compile time. Rather it is a run-time mechanism and it will not prevent the function from actually throwing an exception. Even worse, if an unlisted exception is thrown, it terminates your application with a call to std::terminate()
.
The order of several declarators is actually unordered:
volatile long int const long extern unsigned x;
is the same as
extern const volatile unsigned long long int x;
Considering how unforgiving C++ usually is, I found it somewhat surprising that the standard actually allows you to delete
null pointers.
Objects change type during construction.
In particular, when calling a virtual function from a constructor, you will not be calling the most derived override. Rather, you are calling the implementation from the class you're currently constructing. If that implementation happens to be 0
(pure virtual function), your program will crash at runtime.
A semi-real-world example:
class AbstractBase {
public:
AbstractBase() {
log << "Creating " << className() << endl;
}
protected:
virtual string className() const = 0;
}
class ConcreteGuy {
protected:
virtual string className() const { return "ConcreteGuy"; }
}
Upon constructing a ConcreteGuy
object, the program will terminate with a "calling pure virtual function" error message.
This is why calling virtual functions from constructors is considered evil.
In C++ statements evaluate to something...
int main()
{
"This is a valid C++ program!"
"I will list the first five primes:";
2;
3;
5;
7;
11;
}
It is not very well known that an array initiator may jump in index like in the enumeration definition.
// initializing an array of int
int a[ 7] = { [5]=1, [2]=3, 2};
// resulting in
int a[ 7] = { 0, 0, 3, 2, 0, 1, 0};
// initializing an array of struct
struct { int x,y; } ar[ 4] = { [1].x=23, [3].y=34, [1].y=-1, [1].x=12};
// resulting in
struct { int x,y; } ar[ 4] = { { 0, 0}, { 12, -1}, { 0, 0}, { 0, 34}};
// interesting usage
char forbidden[ 256] = { ['a']=1, ['e']=1, ['i']=1, ['o']=1, ['u']=1};
Most of these are true for C++ also.
精彩评论