C++ puzzles and FAQ
Table of Contents
- Explain the difference between templates in C++ and generics in Java.
- Differences between C++ Classes and Structs
- return statement vs exit() in main()
static_cast
,const_cast
,dynamic_cast
,reinterpret_cast
andC casts
- How to implement dynamiccast?
- std::bind the overloaded function
- instantiate objects from a string holding their class name?
- cc
Explain the difference between templates in C++ and generics in Java.
Differences between C++ Classes and Structs
members and inheritance
that members of a class are private by default, whereas members of a struct are public by default. Inheritance between classes is also private by default, and inheritance between structs is public by default.
a struct inheriting from a class and a class inheriting from a struct
class A { public: int a; }; struct B : A { }; struct C { int c; }; class D : C { }; int main() { B b; D d; b.a = 1; d.c = 2; }
Although a recent version of the GNU project C++ compiler treats the assignment of 2 into d.c in the above example as a compile-time error, a programmer who is more interested in standards compliance should refer to the C++ standard. After all, compilers do not determine standard behavior; standards prescribe standard behavior for compilers. The GNU project C++ compiler is consistent with 11.2.2 of ISO/IEC 14882-2003, which states that the kind of inheritance is determined by the derived class being declared as a class or struct when an access specificer for the base class is absent.
Inheritance differences?1
- a struct is implicitly sealed, a class isn't.
- a struct can't be abstract, a class can.
- a struct can't call : base() in its constructor whereas a class with no explicit base class can.
- a struct can't extend another class, a class can.
- a struct can't declare protected members (eg fields, nested types) a class can.
- a struct can't declare abstract function members, an abstract class can.
- a struct can't declare virtual function members, a class can.
- a struct can't declare sealed function members, a class can.
- a struct can't declare override function members, a class can. The one exception to this rule is that a struct can override the virtual methods of System.Object, viz, Equals(), and GetHashCode(), and ToString().
return statement vs exit() in main()
When exit(0) is used to exit from program, destructors for locally scoped non-static objects are not called. But destructors are called if return 0 is used. Note that static objects will be cleaned up even if we call exit().
#include<iostream> #include<stdio.h> #include<stdlib.h> using namespace std; class Test { public: Test() { printf("Inside Test's Constructor\n"); } ~Test(){ printf("Inside Test's Destructor"); getchar(); } }; int main() { Test t1; // using exit(0) to exit from main exit(0); } Output: Inside Test’s Constructor
#include<iostream> #include<stdio.h> #include<stdlib.h> using namespace std; class Test { public: Test() { printf("Inside Test's Constructor\n"); } ~Test(){ printf("Inside Test's Destructor"); } }; int main() { Test t1; // using return 0 to exit from main return 0; } Output: Inside Test’s Constructor Inside Test’s Destructor
#include<iostream> #include<stdio.h> #include<stdlib.h> using namespace std; class Test { public: Test() { printf("Inside Test's Constructor\n"); } ~Test(){ printf("Inside Test's Destructor"); getchar(); } }; int main() { static Test t1; // Note that t1 is static exit(0); } Output: Inside Test’s Constructor Inside Test’s Destructor
static_cast
, const_cast
, dynamic_cast
, reinterpret_cast
and C casts
static_cast
static_cast
is the first cast you should attempt to use. It does
things like implicit conversions between types (such as int to float,
or pointer to void*), and it can also call explicit conversion
functions (or implicit ones). In many cases, explicitly stating
static_cast
isn't necessary, but it's important to note that the
T(something)
syntax is equivalent to (T)something
and should be
avoided.
static_cast
can also cast through inheritance hierarchies. It is
unnecessary when casting upwards (towards a base class), but when
casting downwards it can be used as long as it doesn't cast through
virtual
inheritance. It does not do checking, however, and it is
undefined behavior to staticcast down a hierarchy to a type that
isn't actually the type of the object.(并不推荐使用于多态对象)。
const_cast
const_cast
can be used to remove or add const
to a variable; no other
C++ cast is capable of removing it (not even reinterpret_cast
). This
can be useful when overloading member functions based on const, for
instance. It can also be used to add const to an object, such as to
call a member function overload.
const_cast
also works similarly on volatile
, though that's less common.
dynamic_cast
主要处理多态。
dynamic_cast
is almost exclusively used for handling polymorphism. You
can cast a pointer or reference to any polymorphic type to any other
class type (a polymorphic type has at least one virtual function,
declared or inherited). You can use it for more than just casting
downwards – you can cast sideways or even up another chain. The
dynamic_cast
will seek out the desired object and return it if
possible.(会检查)If it can't, it will return NULL
in the case of a pointer,
or throw std::bad_cast
in the case of a reference.
dynamic_cast
has some limitations, though. It doesn't work if there
are multiple objects of the same type in the inheritance hierarchy
(the so-called 'dreaded diamond') and you aren't using virtual
inheritance. It also can only go through public inheritance - it will
always fail to travel through protected
or private
inheritance. This
is rarely an issue, however, as such forms of inheritance are rare.
reinterpret_cast
主要处理底层bit的重新释义。
reinterpretcast is the most dangerous cast, and should be used very
sparingly. It turns one type directly into another - such as casting
the value from one pointer to another, or storing a pointer in an int
,
or all sorts of other nasty things. Largely, the only guarantee you
get with reinterpret_cast
is that normally if you cast the result back
to the original type, you will get the exact same value (but not if
the intermediate type is smaller than the original type).
There are a number of conversions that reinterpret_cast
cannot do,
too. It's used primarily for particularly weird conversions and bit
manipulations, like turning a raw data stream into actual data, or
storing data in the low bits of an aligned pointer.
C casts
进行如下的顺序的cast,会隐式用 reinterpret_cast
,比较危险。
C casts are casts using (type)object or type(object). A C-style cast is defined as the first of the following which succeeds:
const_cast
static_cast
static_cast
, thenconst_cast
reinterpret_cast
reinterpret_cast
, thenconst_cast
It can therefore be used as a replacement for other casts in some
instances, but can be extremely dangerous because of the ability to
devolve into a reinterpret_cast
, and the latter should be preferred
when explicit casting is needed, unless you are sure static_cast
will
succeed or reinterpret_cast
will fail. Even then, consider the longer,
more explicit option.
C-style casts also ignore access control when performing a
static_cast
, which means that they have the ability to perform an
operation that no other cast can. This is mostly a kludge, though, and
in my mind is just another reason to avoid C-style casts.
How to implement dynamiccast?
std::bind the overloaded function
#include <iostream> #include <functional> #include <string> using namespace std; class Foo { public: void print(int a) { cout << "int: " << a << endl; } void print(string a) { cout << "string: " << a << endl; } }; double f(double x) { return x; } int main() { function<void(int)> func1; Foo foo; func1 = bind(&Foo::print, foo, placeholders::_1); return 0; }
Compile it will dump below error:
./func_same_name.cc: In function ‘int main()’: ./func_same_name.cc:29:50: error: no matching function for call to ‘bind(<unresolved overloaded function type>, Foo&, const std::_Placeholder<1>&)’ func1 = bind(&Foo::print, foo, placeholders::_1); ^ ./func_same_name.cc:29:50: note: candidates are: In file included from ./func_same_name.cc:2:0:
use static_cast
to disambiguate the address of the overloaded function
- the function
function<double(void)> func = std::bind(static_cast<double(&)(double)>(f), 2.);
- class member function
Foo foo; func = bind(static_cast<void(Foo::*)(int)>(&Foo::print), foo, placeholders::_1);
instantiate objects from a string holding their class name?2
use a map
template<typename T> Base * createInstance() { return new T; } typedef std::map<std::string, Base*(*)()> map_type; map_type map; map["DerivedA"] = &createInstance<DerivedA>; map["DerivedB"] = &createInstance<DerivedB>;
And then you can do
return map[some_string]();
to have the types register themselves
// in base.hpp: template<typename T> Base * createT() { return new T; } struct BaseFactory { typedef std::map<std::string, Base*(*)()> map_type; static Base * createInstance(std::string const& s) { map_type::iterator it = getMap()->find(s); if(it == getMap()->end()) return 0; return it->second(); } protected: static map_type * getMap() { // never delete'ed. (exist until program termination) // because we can't guarantee correct destruction order if(!map) { map = new map_type; } return map; } private: static map_type * map; }; template<typename T> struct DerivedRegister : BaseFactory { DerivedRegister(std::string const& s) { getMap()->insert(std::make_pair(s, &createT<T>)); } }; // in derivedb.hpp class DerivedB { ...; private: static DerivedRegister<DerivedB> reg; }; // in derivedb.cpp: DerivedRegister<DerivedB> DerivedB::reg("DerivedB");
You could decide to create a macro for the registration
#define REGISTER_DEC_TYPE(NAME) \ static DerivedRegister<NAME> reg #define REGISTER_DEF_TYPE(NAME) \ DerivedRegister<NAME> NAME::reg(#NAME)