Modified February 26, 2022
These are questions about C++ Style and Technique that people ask me often.If you have better questions or comments on the answers,feel free to email me (bs at cs dot tamu dot edu).Please remember that I can't spend all of my time improving my homepages.
I have contributed to the new, unified,isocpp.org C++ FAQmaintained byThe C++ Foundationof which I am a director.The maintenance of this FAQ is likely to become increasingly sporadic.
Also, theC++ Core Guidelinescontains a large set of maintained guidelines for the use of modern C++.
For more general questions, see mygeneral FAQ.
For terminology and concepts, see myC++ glossary.
Please note that these are just a collection of questions and answers. They are nota substitute for a carefully selected sequence of examples and explanationsas you would find in a good textbook. Nor do they offer detailed and precisespecifications as you would find in a reference manual or the standard.See The Design and Evolution of C++ for questionsrelated to the design of C++.SeeThe C++ Programming Language for questionsabout the use of C++ and its standard library.
Translations:
#include<iostream>#include<vector>#include<algorithm>using namespace std;int main(){vector<double> v;double d;while(cin>>d) v.push_back(d);// read elementsif (!cin.eof()) {// check if input failedcerr<< "format error\n";return 1;// error return}cout<< "read "<< v.size()<< " elements\n";reverse(v.begin(),v.end());cout<< "elements in reverse order:\n";for (int i = 0; i<v.size(); ++i) cout<< v[i]<< '\n';return 0; // success return}Here are a few observations about this program:#include<iostream>#include<vector>#include<algorithm>#include<string>using namespace std;int main(){vector<double> v;double d;while(cin>>d) v.push_back(d);// read elementsif (!cin.eof()) {// check if input failedcin.clear();// clear error statestring s;cin >> s;// look for terminator stringif (s != "end") {cerr<< "format error\n";return 1;// error return}}cout<< "read "<< v.size()<< " elements\n";reverse(v.begin(),v.end());cout<< "elements in reverse order:\n";for (int i = 0; i<v.size(); ++i) cout<< v[i]<< '\n';return 0; // success return}The main point of a C++ coding standard is to provide a set of rules for usingC++ for a particular purpose in a particular environment. It follows that therecannot be one coding standard for all uses and all users.For a given application (or company, application area, etc.), a good coding standardis better than no coding standard. On the other hand, I have seen many examples thatdemonstrate that a bad coding standard is worse than no coding standard.
Don't use C coding standards (even if slightly modified for C++) and don't use ten-year-oldC++ coding standards (even if good for their time).C++ isn't (just) C and Standard C++ is not (just) pre-standard C++.
However, it is more likely that the program that you are trying to compileis poorly designed, so that compiling it involves the compiler examininghundreds of header files and tens of thousands of lines of code.In principle, this can be avoided.If this problem is in your library vendor's design, there isn't much youcan do (except changing to a better library/vendor), but you can structureyour own code to minimize re-compilation after changes.Designs that do that are typically better, more maintainable, designs becausethey exhibit better separation of concerns.
Consider a classical example of an object-oriented program:
class Shape {public:// interface to users of Shapesvirtual void draw() const;virtual void rotate(int degrees);// ...protected:// common data (for implementers of Shapes)Point center;Color col;// ...};class Circle : public Shape {public:void draw() const;void rotate(int) { }// ...protected:int radius;// ...};class Triangle : public Shape {public:void draw() const;void rotate(int);// ...protected:Point a, b, c;// ...};The idea is that users manipulate shapes through Shape's public interface,and that implementers of derived classes (such as Circle and Triangle)share aspects of the implementation represented by the protected members.There are three serious problems with this apparently simple idea:
Thus, the presence of "information helpful to implementers" in the base classthat also acts as the interface to users is the source of instability in theimplementation, spurious recompilation of user code (when implementationinformation changes), and excess inclusion of header files into user code(because the "information helpful to implementers" needs those headers).This is sometimes known as the "brittle base class problem."
The obvious solution is to omit the "information helpful to implemeters" forclasses that are used as interfaces to users. That is, to make interfaces,pure interfaces. That is, to represent interfaces as abstract classes:
class Shape {public:// interface to users of Shapesvirtual void draw() const = 0;virtual void rotate(int degrees) = 0;virtual Point center() const = 0;// ...// no data};class Circle : public Shape {public:void draw() const;void rotate(int) { }Point center() const { return cent; }// ...protected:Point cent;Color col;int radius;// ...};class Triangle : public Shape {public:void draw() const;void rotate(int);Point center() const;// ...protected:Color col;Point a, b, c;// ...};The users are now insulated from changes to implementations of derived classes.I have seen this technique decrease build times by orders of magnitudes.But what if there really is some information that is common to all derivedclasses (or simply to several derived classes)?Simply make that information a class and derive the implementation classesfrom that also:
class Shape {public:// interface to users of Shapesvirtual void draw() const = 0;virtual void rotate(int degrees) = 0;virtual Point center() const = 0;// ...// no data};struct Common {Color col;// ...};class Circle : public Shape, protected Common {public:void draw() const;void rotate(int) { }Point center() const { return cent; }// ...protected:Point cent;int radius;};class Triangle : public Shape, protected Common {public:void draw() const;void rotate(int);Point center() const;// ...protected:Point a, b, c;};class Empty { };void f(){Empty a, b;if (&a == &b) cout<< "impossible: report error to compiler supplier";Empty* p1 = new Empty;Empty* p2 = new Empty;if (p1 == p2) cout<< "impossible: report error to compiler supplier";}There is an interesting rule that says that an empty base class neednot be represented by a separate byte:struct X : Empty {int a;// ...};void f(X* p){void* p1 = p;void* p2 = &p->a;if (p1 == p2) cout<< "nice: good optimizer";}This optimization is safe and can be most useful. It allows a programmerto use empty classes to represent very simple concepts without overhead.Some current compilers provide this "empty base class optimization".Sometimes, you do want to have representation data in a class.Consider class complex:
template<class Scalar> class complex {public:complex() : re(0), im(0) { }complex(Scalar r) : re(r), im(0) { }complex(Scalar r, Scalar i) : re(r), im(i) { }// ...complex& operator+=(const complex& a){ re+=a.re; im+=a.im; return *this; }// ...private:Scalar re, im;};This type is designed to be used much as a built-in type and the representationis needed in the declaration to make it possible to create genuinely localobjects (i.e. objects that are allocated on the stack and not on a heap) andto ensure proper inlining of simple operations. Genuinely local objects andinlining is necessary to get the performance of complex close to what isprovided in languages with a built-in complex type.Also, objects of a class with a virtual function require space needed by the virtual function call mechanism - typically one word per object. This overheadcan be significant, and can get in the way of layout compatibility withdata from other languages (e.g. C and Fortran).
See The Design and Evolution of C++ for moredesign rationale.
So when should I declare a destructor virtual? Whenever the class has atleast one virtual function.Having virtual functions indicate that a class is meant to act as aninterface to derived classes, and when it is, an object of a derived classmay be destroyed through a pointer to the base.For example:
class Base {// ...virtual ~Base();};class Derived : public Base {// ...~Derived();};void f(){Base* p = new Derived;delete p;// virtual destructor used to ensure that ~Derived is called}Had Base's destructor not been virtual, Derived's destructor would not havebeen called - with likely bad effects, such as resources owned by Derived notbeing freed.Techniques for using an indirection when you ask to create an object areoften referred to as "Virtual constructors". For example, see TC++PL3 15.6.2.
For example, here is a technique for generating an object of an appropriatetype using an abstract class:
struct F {// interface to object creation functionsvirtual A* make_an_A() const = 0;virtual B* make_a_B() const = 0;};void user(const F& fac){A* p = fac.make_an_A();// make an A of the appropriate typeB* q = fac.make_a_B();// make a B of the appropriate type// ...}struct FX : F {A* make_an_A() const { return new AX();} // AX is derived from AB* make_a_B() const { return new BX();} // BX is derived from B};struct FY : F {A* make_an_A() const { return new AY();} // AY is derived from AB* make_a_B() const { return new BY();} // BY is derived from B};int main(){FX x;FY y;user(x);// this user makes AXs and BXsuser(y);// this user makes AYs and BYsuser(FX());// this user makes AXs and BXsuser(FY());// this user makes AYs and BYs// ...}This is a variant of what is often called "the factory pattern".The point is that user() is completely isolated from knowledge of classessuch as AX and AY.class Base {public:void f1();// not virtualvirtual void f2();// virtual, not purevirtual void f3() = 0;// pure virtual};Base b;// error: pure virtual f3 not overriddenHere, Base is an abstract class (because it has a pure virtual function), so no objects of class Basecan be directly created: Base is (explicitly) meant to be a base class.For example:class Derived : public Base {// no f1: fine// no f2: fine, we inherit Base::f2void f3();};Derived d;// ok: Derived::f3 overrides Base::f3Abstract classes are immensely useful for defining interfaces. In fact, a class with only purevirtual functions is often called an interface.You can define a pure virtual function:
Base::f3() { /* ... */ }This is very occasionally useful(to provide some simple common implementation detail for derived classes),but Base::f3() must still be overridden in some derived class.If you don't override a pure virtual function in a derived class, that derived class becomes abstract:
class D2 : public Base {// no f1: fine// no f2: fine, we inherit Base::f2// no f3: fine, but D2 is therefore still abstract};D2 d;// error: pure virtual Base::f3 not overridden#include<iostream>using namespace std;class B {public:int f(int i) { cout<< "f(int): "; return i+1; }// ...};class D : public B {public:double f(double d) { cout<< "f(double): "; return d+1.3; }// ...};int main(){D* pd = new D;cout<< pd->f(2)<< '\n';cout<< pd->f(2.3)<< '\n';}which will produce:f(double): 3.3f(double): 3.6rather than the
f(int): 3f(double): 3.6that some people (wrongly) guessed.
In other words, there is no overload resolution between D and B. The compilerlooks into the scope of D, finds the single function "double f(double)" andcalls it. It never bothers with the (enclosing) scope of B. In C++, there isno overloading across scopes - derived class scopes are not an exception tothis general rule. (SeeD&E orTC++PL3 for details).
But what if I want to create an overload set of all my f() functions frommy base and derived class? That's easily done using a using-declaration:
class D : public B {public:using B::f;// make every f from B availabledouble f(double d) { cout<< "f(double): "; return d+1.3; }// ...};Give that modification, the output will bef(int): 3f(double): 3.6That is, overload resolution was applied to B's f() and D's f() to selectthe most appropriate f() to call.
Sort of, but don't do it blindly and there are often superior alternatives.Consider:
void compute(cmplx z, double d){cmplx z2 = z+d;// c++ stylez2 = f(z2);// use z2cmplx& z3 = *new cmplx(z+d);// Java style (assuming Java could overload +)z3 = f(z3);delete &z3;}The clumbsy use of "new" for z3 is unnecessary and slow compared withthe idiomatic use of a local variable (z2).You don't need to use "new" to create an object if you also "delete" that object in the same scope;such an object should be a local variable.Consider:
#include<string>#include<iostream>using namespace std;class B {public:B(const string& ss) { cout<< "B constructor\n"; f(ss); }virtual void f(const string&) { cout<< "B::f\n";}};class D : public B {public:D(const string & ss) :B(ss) { cout<< "D constructor\n";}void f(const string& ss) { cout<< "D::f\n"; s = ss; }private:string s;};int main(){D d("Hello");}the program compiles and produceB constructorB::fD constructorNotenot D::f.Consider what would happen if the rule were different so that D::f() wascalled from B::B(): Because the constructor D::D() hadn't yet been run,D::f() would try to assign its argument to an uninitialized string s.The result would most likely be an immediate crash.
Destruction is done "derived class before base class", so virtual functionsbehave as in constructors: Only the local definitions are used - and nocalls are made to overriding functions to avoid touching the (now destroyed)derived class part of the object.
For more details seeD&E 13.2.4.2 orTC++PL3 15.4.3.
It has been suggested that this rule is an implementation artifact. It isnot so. In fact, it would be noticeably easier to implement the unsafe ruleof calling virtual functions from constructors exactly as from other functions.However, that would imply that no virtual function could be written to relyon invariants established by base classes. That would be a terrible mess.
Consider placement new used to place objects in a set of arenas
class Arena { public: void* allocate(size_t); void deallocate(void*); // ... }; void* operator new(size_t sz, Arena& a) { return a.allocate(sz); } Arena a1(some arguments); Arena a2(some arguments);Given that, we can writeX* p1 = new(a1) X; Y* p2 = new(a1) Y; Z* p3 = new(a2) Z; // ...But how can we later delete those objects correctly? The reason that there isno built-in "placement delete" to match placement new is that there is nogeneral way of assuring that it would be used correctly. Nothing in the C++type system allows us to deduce that p1 points to an object allocated inArena a1. A pointer to any X allocated anywhere can be assigned to p1.
However, sometimes the programmer does know, and there is a way:
template<class T> void destroy(T* p, Arena& a) { if (p) { p->~T();// explicit destructor call a.deallocate(p); } }Now, we can write:destroy(p1,a1); destroy(p2,a2); destroy(p3,a3);If an Arena keeps track of what objects it holds, you can even write destroy()to defend itself against mistakes.
It is also possible to define a matching operator new() and operator delete()pairs for a class hierarchyTC++PL(SE) 15.6.See alsoD&E 10.4 and TC++PL(SE) 19.4.5.
Yes, but why do you want to? There are two common answers:
If there is a genuine need for "capping" a class hierarchy to avoid virtualfunction calls, one might ask why those functions are virtual in the firstplace. I have seen examples where performance-critical functions had beenmade virtual for no good reason, just because "that's the way we usually do it".
The other variant of this problem, how to prevent derivation for logicalreasons, has a solution in C++11.For example:
struct Base {virtual void f();};struct Derived final : Base {// now Derived is final; you cannot derive from itvoid f() override;};struct DD: Derived {// error: Derived is final// ...};For older compilers, you can use a somewhat clumsy technique:class Usable;class Usable_lock {friend class Usable;private:Usable_lock() {}Usable_lock(const Usable_lock&) {}};class Usable : public virtual Usable_lock {// ...public:Usable();Usable(char*);// ...};Usable a;class DD : public Usable { };DD dd; // error: DD::DD() cannot access // Usable_lock::Usable_lock(): private membervector<int> vi(10);vector<Shape*> vs;list<string> lst;list<double> l2map<string,Record*> tbl;map< Key,vector<Record*> > t2;These containers are described in all good C++ textbooks, and should be preferred overarraysand "home cooked" containers unless there is a good reason not to.
These containers are homogeneous; that is, they hold elements of the same type. If you wanta container to hold elements of several different types, you must express that either as a unionor (usually much better) as a container of pointers to a polymorphic type.The classical example is:
vector<Shape*> vi;// vector of pointers to ShapesHere, vi can hold elements of any type derived from Shape. That is, vi is homogeneous in thatall its elements are Shapes (to be precise, pointers to Shapes) and heterogeneous in the sensethat vi can hold elements of a wide variety of Shapes, such as Circles, Triangles, etc.
So, in a sense all containers (in every language) are homogenous because to use them there mustbe a common interface to all elements for users to rely on. Languages that provide containersdeemed heterogenous simply providecontainers of elements that all provide a standard interface. For example, Java collections providecontainers of (references to) Objects and you use the (common) Object interface to discover thereal type of an element.
The C++ standard library provides homogeneous containers because those are the easiest to use in thevast majority of cases, gives the best compile-time error message, and imposes no unnecessaryrun-time overheads.
If you need a heterogeneous container in C++, define a common interface for all the elements andmake a container of those. For example:
class Io_obj { /* ... */ };// the interface needed to take part in object I/Ovector<Io_obj*> vio;// if you want to manage the pointers directlyvector< Handle<Io_obj> > v2;// if you want a "smart pointer" to handle the objectsDon't drop to the lowest level of implementation detail unless you have to:vector<void*> memory;// rarely neededA good indication that you have "gone too low level" is that your code gets littered with casts.
Using an Any class, such as Boost::Any, can be an alternative in some programs:
vector<Any> v;
Let's look at those problems in turn.Often, a vector<X> is slower than somebody's specialized My_container<X>because My_container<X> is implemented as a container of pointers to X.The standard containers hold copies of values, and copy a value when you put it into the container.This is essentially unbeatable for small values, but can be quite unsuitable for huge objects:
vector<int> vi;vector<Image> vim;// ...int i = 7;Image im("portrait.jpg");// initialize image from file// ...vi.push_back(i);// put (a copy of) i into vivim.push_back(im);// put (a copy of) im into vimNow, if portrait.jpg is a couple of megabytes and Image has value semantics(i.e., copy assignment and copy construction make copies) then vim.push_back(im) will indeedbe expensive.But -- as the saying goes -- if it hurts so much, just don't do it.Instead, either use a container of handlesor a containers of pointers. For example, if Image had reference semantics, the code abovewould incur only the cost of a copy constructor call, which would be trivial compared to mostimage manipulation operators.If some class, say Image again, does have copy semantics for good reasons, a container of pointersis often a reasonable solution:vector<int> vi;vector<Image*> vim;// ...Image im("portrait.jpg");// initialize image from file// ...vi.push_back(7);// put (a copy of) 7 into vivim.push_back(&im);// put (a copy of) &im into vimNaturally, if you use pointers, you have to think about resource management,but containers of pointers can themselves be effective and cheap resource handles(often, you need a container with a destructor for deleting the "owned" objects).The second frequently occuring genuine performance problem is the use of a map<string,X> fora large number of (string,X) pairs.Maps are fine for relatively small containers(say a few hundred or few thousand elements -- access to an element of a map of 10000elements costs about 9 comparisons), where less-than is cheap, and where nogood hash-function can be constructed. If you have lots of strings and a good hash function,use a hash table.The unordered_map from the standard committee's Technical Report is now widelyavailable and is far better than most people's homebrew.
Sometimes, you can speed up things by using (const char*,X) pairs rather than (string,X) pairs,but remember that< doesn't do lexicographical comparison for C-style strings. Also, if X is large,you may have the copy problem also (solve it in one of the usual ways).
Intrusive lists can be really fast.However, consider whether you need a list at all: a vector is morecompact and is therefore smaller and faster in many cases - even when you do inserts and erases.For example, if you logically have a list of a few integer elements, a vector is significantlyfaster than a list (any list).Also, intrusive lists cannot hold built-in types directly (an int does not have a link member).So, assume that you really needa list and that you can supply a link field for every element type. The standard-library listby default performs an allocation followed by a copy for each operation inserting an element(and a deallocation for each operation removing an element). For std::list with thedefault allocator, this can be significant. For small elements where the copy overhead is notsignificant, consider using an optimized allocator. Use a hand-crafted intrusive lists onlywhere a list and the last ounce of performance is needed.
People sometimes worry about the cost of std::vector growing incrementally.I used to worry about that and used reserve() to optimize the growth.After measuring my code and repeatedly having trouble finding the performance benefits of reserve()in real programs,I stopped using it except where it is needed to avoid iterator invalidation (a rare case in my code).Again: measure before you optimize.
class X {int i;public:void m();// grant X::m() accessfriend void f(X&);// grant f(X&) access// ...};void X::m() { i++; /* X::m() can access X::i */ }void f(X& x) { x.i++; /* f(X&) can access X::i */ }For a description on the C++ protection model, seeD&E sec 2.10andTC++PL sec 11.5, 15.3, and C.11.struct Point {int x,y;Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }};Point p1(1,2);Point p2 = p1;Here we get p2.x==p1.x and p2.y==p1.y.That's often exactly what you want (and essential for C compatibility), but consider:class Handle {private:string name;X* p;public:Handle(string n):name(n), p(0) { /* acquire X called "name" and let p point to it */ }~Handle() { delete p; /* release X called "name" */ }// ...};void f(const string& hh){Handle h1(hh);Handle h2 = h1;// leads to disaster!// ...}Here, the default copy gives us h2.name==h1.name and h2.p==h1.p.This leads to disaster: when we exit f() the destructors for h1 and h2 are invoked and the objectpointed to by h1.p and h2.p is deleted twice.How do we avoid this?The simplest solution is to prevent copying by making the operations that copy private:
class Handle {private:string name;X* p;Handle(const Handle&);// prevent copyingHandle& operator=(const Handle&);public:Handle(string n):name(n), p(0) { /* acquire the X called "name" and let p point to it */ }~Handle() { delete p; /* release X called "name" */ }// ...};void f(const string& hh){Handle h1(hh);Handle h2 = h1;// error (reported by compiler)// ...}If we need to copy,we can of course define the copy initializer and the copy assignment to provide the desired semantics.Now return to Point.For Point the default copy semantics is fine, the problem is the constructor:
struct Point {int x,y;Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }};void f(Point);void g(){Point orig;// create orig with the default value (0,0)Point p1(2);// create p1 with the default y-coordinate 0f(2);// calls Point(2,0);}People provide default arguments to get the convenience used for orig and p1.Then, some are surprised by the conversion of 2 to Point(2,0) in the call of f().A constructor taking a single argument defines a conversion.By default that's an implicit conversion.To require such a conversion to be explicit, declare the constructor explicit:struct Point {int x,y;explicit Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }};void f(Point);void g(){Point orig;// create orig with the default value (0,0)Point p1(2);// create p1 with the default y-coordinate 0// that's an explicit call of the constructorf(2);// error (attmpted implicit conversion)Point p2 = 2;// error (attmpted implicit conversion)Point p3 = Point(2);// ok (explicit conversion)}void f1(const complex* x, const complex* y)// without references{complex z = *x+*y;// ugly// ...}void f2(const complex& x, const complex& y)// with references{complex z = x+y;// better// ...}More generally, if you want to have both the functionality of pointers and the functionality of references, you need either two different types (as in C++)or two different sets of operations on a single type.For example, with a single type you need both an operation to assign to theobject referred to and an operation to assign to the reference/pointer.This can be done using separate operators (as in Simula). For example:Ref<My_type> r :- new My_type;r := 7;// assign to objectr :- new My_type;// assign to referenceAlternatively, you could rely on type checking (overloading).For example:
Ref<My_type> r = new My_type;r = 7;// assign to objectr = new My_type;// assign to reference
Why would I want to change an argument?Well, often we have to, but often we have an alternative: produce a new value. Consider:
void incr1(int& x);// incrementint incr2(int x);// incrementint v = 2;incr1(v);// v becomes 3v = incr2(v);// v becomes 4I think that for a reader, incr2() is easier to understand.That is, incr1() is more likely to lead to mistakes and errors.So, I'd prefer the style that returns a new value over the one that modifies a valueas long as the creation and copy of a new value isn't expensive.
I do want to change the argument, should I use a pointer or should I use a reference?I don't know a strong logical reason.If passing ``not an object'' (e.g. a null pointer) is acceptable, using a pointer makes sense.My personal style is to use a pointer when I want to modify an object because in some contextsthat makes it easier to spot that a modification is possible.
Note also that a call of a member function is essentially a call-by-reference on the object,so we often use member functions when we want to modify the value/state of an object.
In terms of time and space,an array is just about the optimal construct for accessing a sequence of objects in memory. It is, however, also a very low level data structure with a vast potential for misuse and errorsand in essentially all cases there are better alternatives. By "better" I mean easier to write,easier to read, less error prone, and as fast.
The two fundamental problems with arrays are that
void f(int a[], int s){// do something with a; the size of a is sfor (int i = 0; i<s; ++i) a[i] = i;}int arr1[20];int arr2[10];void g(){f(arr1,20);f(arr2,20);}The second call will scribble all over memory that doesn't belong to arr2.Naturally, a programmer usually get the size right, but it's extra workand ever so often someone makes the mistake.I prefer the simpler and cleaner version using the standard library vector:void f(vector<int>& v){// do something with vfor (int i = 0; i<v.size(); ++i) v[i] = i;}vector<int> v1(20);vector<int> v2(10);void g(){f(v1);f(v2);}Since an array doesn't know its size, there can be no array assignment:
void f(int a[], int b[], int size){a = b;// not array assignmentmemcpy(a,b,size);// a = b// ...}Again, I prefer vector:void g(vector<int>& a, vector<int>& b, int size){a = b;// ...}Another advantage of vector here is that memcpy() is not going to do the right thing forelements with copy constructors, such as strings.void f(string a[], string b[], int size){a = b;// not array assignmentmemcpy(a,b,size);// disaster// ...}void g(vector<string>& a, vector<string>& b, int size){a = b;// ...}An array is of a fixed size determined at compile time:
const int S = 10;void f(int s){int a1[s];// errorint a2[S];// ok// if I want to extend a2, I'll have to change to an array// allocated on free store using malloc() and use realloc()// ...}To contrast:const int S = 10;void g(int s){vector<int> v1(s);// okvector<int> v2(S);// okv2.resize(v2.size()*2);// ...}C99 allows variable array bounds for local arrays, but those VLAs have their own problems.The way that array names "decay" into pointers is fundamental to their use in C and C++.However, array decay interact very badly with inheritance. Consider:
class Base { void fct(); /* ... */ };class Derived : Base { /* ... */ };void f(Base* p, int sz){for (int i=0; i<sz; ++i) p[i].fct();}Base ab[20];Derived ad[20];void g(){f(ab,20);f(ad,20);// disaster!}In the last call, the Derived[] is treated as a Base[] and the subscripting no longer works correctly when sizeof(Derived)!=sizeof(Base) -- as will be the case in most cases of interest.If we used vectors instead, the error would be caught at compile time:void f(vector<Base>& v){for (int i=0; i<v.size(); ++i) v[i].fct();}vector<Base> ab(20);vector<Derived> ad(20);void g(){f(ab);f(ad);// error: cannot convert a vector<Derived> to a vector<Base>}I find that an astonishing number of novice programming errors in C and C++ relate to(mis)uses of arrays.If you have to name the null pointer, call it nullptr; that's what it's called in C++11.Then, "nullptr" will be a keyword.
Basically, C++ constructs objects simply by concatenating sub objects. Thus
struct A { int a,b; };is represented by two ints next to each other, and struct B : A { int c; };is represented by an A followed by an int; that is, by three ints next toeach other.Virtual functions are typically implemented by adding a pointer (the vptr)to each object of a class with virtual functions. This pointer points tothe appropriate table of functions (the vtbl). Each class has its own vtblshared by all objects of that class.
v[i] = i++;Related example:
f(v[i],i++);Here, the result is undefined because the order of evaluation of function arguments are undefined.
Having the order of evaluation undefined is claimed to yield better performing code.Compilers could warn about such examples, which are typically subtle bugs (or potential subtle bugs).I'm disappointed that after decades, most compilers still don't warn, leaving that job tospecialized, separate, and underused tools.
Because machines differ and because C left many things undefined.For details, including definitionsof the terms "undefined", "unspecified", "implementation defined", and "well-formed";see the ISO C++ standard.Note that the meaning of those terms differ from their definition of the ISO C standardand from some common usage.You can get wonderfully confused discussions when people don't realize that not everybodyshare definitions.
This is a correct, if unsatisfactory, answer.Like C, C++ is meant to exploit hardware directly and efficiently. This implies that C++ must dealwith hardware entities such as bits, bytes, words, addresses, integer computations, andfloating-point computations the way they are on a given machine, rather than how we mightlike them to be.Note that many "things" that people refer to as "undefined" are in fact "implementation defined",so that we can write perfectly specified code as long as we know which machine we are running on.Sizes of integers and the rounding behaviour of floating-point computations fall into that category.
Consider what is probably the the best known and most infamous example of undefined behavior:
int a[10];a[100] = 0;// range errorint* p = a;// ...p[100] = 0;// range error (unless we gave p a better value before that assignment)The C++ (and C) notion of array and pointer are direct representations of a machine's notionof memory and addresses, provided with no overhead. The primitive operations on pointers mapdirectly onto machine instructions. In particular, no range checking is done. Doing range checkingwould impose a cost in terms of run time and code size. C was designed to outcompete assembly codefor operating systems tasks, so that was a necessary decision. Also, C -- unlike C++ --has no reasonable way of reporting a violation had a compiler decided to generate code to detect it:There are no exceptions in C.C++ followed C for reasons ofcompatibility and because C++ also compete directly with assembler (in OS, embedded systems, andsome numeric computation areas).If you want range checking, use a suitable checked class (vector, smart pointer, string, etc.).A good compiler could catch the range error for a[100] at compile time, catching the one for p[100]is far more difficult, and in general it is impossible to catch every range error at compile time.
Other examples of undefined behavior stems from the compilation model.A compiler cannot detect an inconsistent definition of an object or a function in separately-compiledtranslation units. For example:
// file1.c:struct S { int x,y; };int f(struct S* p) { return p->x; }// file2.c:struct S { int y,x; }int main(){struct S s;s.x = 1;int x = f(&s);// x!=s.x !!return 2;}Compiling file1.c and file2.c and linking the results into the same program is illegalin both C and C++.A linker could catch the inconsistent definition of S, but is not obliged to do so (and most don't).In many cases, it can be quite difficult to catch inconsistencies between separatelycompiled translation units.Consistent use of header files helps minimize such problems and there are some signs that linkersare improving.Note that C++ linkers do catch almost all errors related to inconsistently declaredfunctions.Finally, we have the apparently unnecessary and rather annoying undefined behavior of individualexpressions. For example:
void out1() { cout<< 1; }void out2() { cout<< 2; }int main(){int i = 10;int j = ++i + i++;// value of j unspecifiedf(out1(),out2());// prints 12 or 21}The value of j is unspecified to allow compilers to produce optimal code. It is claimed that thedifference between what can be produced giving the compiler this freedom and requiring"ordinary left-to-right evaluation" can be significant. I'm unconvinced, but with innumerable compilers"out there" taking advantage of the freedom and some people passionately defending that freedom, achange would be difficult and could take decades to penetrate to the distant corners of the C and C++worlds.I am disappointed that not all compilers warn against code such as ++i+i++.Similarly, the order of evaluation of arguments is unspecified.IMO far too many "things" are left undefined, unspecified, implementation-defined, etc.However, that's easy to say and even to give examples of, but hard to fix.It should also be noted that it is not all that difficult to avoid most of the problems and produceportable code.
Consider:
template<class Container> void draw_all(Container& c) { for_each(c.begin(),c.end(),mem_fun(&Shape::draw)); }If there is a type error, it will be in the resolution of the fairlycomplicated for_each() call. For example, if the element type of thecontainer is an int,then we get some kind of obscure error related to the for_each()call (because we can't invoke Shape::draw() for an int).To catch such errors early, I can write:
template<class Container> void draw_all(Container& c) { Shape* p = c.front(); // accept only containers of Shape*s for_each(c.begin(),c.end(),mem_fun(&Shape::draw)); }The initialization of the spurious variable "p" will trigger a comprehensibleerror message from most current compilers. Tricks like this are commonin all languages and have to be developed for all novel constructs.In production code, I'd probably write something like:template<class Container> void draw_all(Container& c) { typedef typename Container::value_type T; Can_copy<T,Shape*>(); // accept containers of only Shape*s for_each(c.begin(),c.end(),mem_fun(&Shape::draw)); }This makes it clear that I'm making an assertion.The Can_copy template can be defined like this:template<class T1, class T2> struct Can_copy {static void constraints(T1 a, T2 b) { T2 c = a; b = a; }Can_copy() { void(*p)(T1,T2) = constraints; }};Can_copy checks (at compile time) that a T1 can be assigned to a T2.Can_copy<T,Shape*> checks that T is a Shape* or a pointer toa class publicly derived from Shape or a type with a user-defined conversionto Shape*.Note that the definition is close to minimal:Note also that the definition has the desirable properties that
So why is something like Can_copy() - or something even more elegant - not inthe language?D&Econtains an analysis of the difficulties involved in expressing generalconstraints for C++.Since then, many ideas have emerged for making these constraints classeseasier to write and still trigger good error messages. For example,I believe the use of a pointer to function the way I do in Can_copyoriginates with Alex Stepanov and Jeremy Siek.I don't think thatCan_copy() is quite ready for standardization - it needs more use.Also, different forms of constraints are in use in the C++ community;there is not yet a consensus on exactly what form of constraints templatesis the most effective over a wide range of uses.
However, the idea is very general, more general than language facilitiesthat have been proposed and provided specifically for constraints checking.After all, when we writea template we have the full expressive power of C++ available.Consider:
template<class T, class B> struct Derived_from {static void constraints(T* p) { B* pb = p; }Derived_from() { void(*p)(T*) = constraints; }};template<class T1, class T2> struct Can_copy {static void constraints(T1 a, T2 b) { T2 c = a; b = a; }Can_copy() { void(*p)(T1,T2) = constraints; }};template<class T1, class T2 = T1> struct Can_compare {static void constraints(T1 a, T2 b) { a==b; a!=b; a<b; }Can_compare() { void(*p)(T1,T2) = constraints; }};template<class T1, class T2, class T3 = T1> struct Can_multiply {static void constraints(T1 a, T2 b, T3 c) { c = a*b; }Can_multiply() { void(*p)(T1,T2,T3) = constraints; }};struct B { };struct D : B { };struct DD : D { };struct X { };int main(){Derived_from<D,B>();Derived_from<DD,B>();Derived_from<X,B>();Derived_from<int,B>();Derived_from<X,int>();Can_compare<int,float>();Can_compare<X,B>();Can_multiply<int,float>();Can_multiply<int,float,double>();Can_multiply<B,X>();Can_copy<D*,B*>();Can_copy<D,B*>();Can_copy<int,B*>();}// the classical "elements must derived from Mybase*" constraint:template<class T> class Container : Derived_from<T,Mybase> {// ...};Actually, Derived_from doesn't check derivation, but conversion, but that'soften a better constraint.Finding good names for constraints can be hard.qsort(array,asize,sizeof(elem),elem_compare);looks pretty weird, and is harder to understand than
sort(vec.begin(),vec.end());To an expert, the fact that sort()tends to be faster than qsort()for the same elements and the same comparison criteria is often significant.Also, sort() is generic, so that it can be used for any reasonablecombination of container type, element type, and comparison criterion.For example:
struct Record {string name;// ...};struct name_compare {// compare Records using "name" as the keybool operator()(const Record& a, const Record& b) const{ return a.name<b.name; }};void f(vector<Record>& vs){sort(vs.begin(), vs.end(), name_compare());// ...}In addition, most people appreciate that sort() is type safe, that no castsare required to use it, and that they don't have to write a compare() function forstandard types.
For a more detailed explanation, see my paper"Learning C++ as a New language", which you can download from mypublications list.
The primary reason that sort() tends to outperform qsort() is that thecomparison inlines better.
A function object is a more general conceptthan a function because a function objectcan have state that persist across several calls (like a static local variable)and can be initialized and examined from outside the object (unlike a staticlocal variable).For example:
class Sum {int val;public:Sum(int i) :val(i) { }operator int() const { return val; }// extract valueint operator()(int i) { return val+=i; }// application};void f(vector<int> v){Sum s = 0;// initial value 0s = for_each(v.begin(), v.end(), s);// gather the sum of all elementscout<< "the sum is "<< s<< "\n";// or even:cout<< "the sum is "<< for_each(v.begin(), v.end(), Sum(0))<< "\n";}Note that a function object with an inline application operator inlinesbeautifully because there are no pointers involved that might confuseoptimizers. To contrast: current optimizers are rarely (never?) able toinline a call through a pointer to function.Function objects are extensively used to provide flexibility in the standardlibrary.
Clearly, if your code has newoperations, delete operations, and pointer arithmetic all over the place,you are going to mess upsomewhere and get leaks, stray pointers, etc.This is true independently of how conscientious you are with your allocations:eventually the complexityof the code will overcome the time and effort you can afford.It follows that successfultechniques rely on hiding allocation and deallocation inside more manageabletypes.Good examples are the standard containers. They manage memory for theirelements better than you could without disproportionate effort.Consider writing this without the help of string and vector:
#include<vector>#include<string>#include<iostream>#include<algorithm>using namespace std;int main()// small program messing around with strings{cout<< "enter some whitespace-separated words:\n";vector<string> v;string s;while (cin>>s) v.push_back(s);sort(v.begin(),v.end());string cat;typedef vector<string>::const_iterator Iter;for (Iter p = v.begin(); p!=v.end(); ++p) cat += *p+"+";cout<< cat<< '\n';}What would be your chance of getting it right the first time? And how wouldyou know you didn't have a leak?Note the absence of explicit memorymanagement, macros, casts, overflow checks, explicit size limits, andpointers. By using a function object and a standard algorithm, I could haveeliminated the pointer-like use of the iterator, but that seemed overkillfor such a tiny program.
These techniques are not perfect and it is not always easy to use themsystematically. However, they apply surprisingly widely and by reducingthe number of explicit allocations and deallocations you make the remainingexamples much easier to keep track of.As early as 1981, I pointed out that by reducing the number of objects thatI had to keep track of explicitly from many tens of thousands to a few dozens,I had reduced the intellectual effort needed to get the program right froma Herculean task to something manageable, or even easy.
If your application area doesn't have libraries that make programming thatminimizes explicit memory management easy, then the fastest way of gettingyour program complete and correct might be to first build such a library.
Templates and the standard libraries make this use of containers, resourcehandles, etc., much easier than it was even a few years ago. The use ofexceptions makes it close to essential.
If you cannot handle allocation/deallocation implicitly as part of an objectyou need in your application anyway, you can use a resource handleto minimize the chance of a leak.Here is an example where I need to return an object allocated on the free storefrom a function.This is an opportunity to forget to delete that object.After all, we cannot tell just looking at pointer whether it needs to bedeallocated and if so who is responsible for that.Using a resource handle, here the standard library auto_ptr, makes it clearwhere the responsibility lies:
#include<memory>#include<iostream>using namespace std;struct S {S() { cout<< "make an S\n"; }~S() { cout<< "destroy an S\n"; }S(const S&) { cout<< "copy initialize an S\n"; }S& operator=(const S&) { cout<< "copy assign an S\n"; }};S* f(){return new S;// who is responsible for deleting this S?};auto_ptr<S> g(){return auto_ptr<S>(new S);// explicitly transfer responsibility for deleting this S}int main(){cout<< "start main\n";S* p = f();cout<< "after f() before g()\n";//S* q = g();// this error would be caught by the compilerauto_ptr<S> q = g();cout<< "exit main\n";// leaks *p// implicitly deletes *q}Think about resources in general, rather than simply about memory.
If systematic application of these techniques is not possible in yourenvironment (you have to use code from elsewhere, part of your programwas written by Neanderthals, etc.), be sure to use a memory leak detectoras part of your standard development procedure, or plug in a garbagecollector.
Basically, someone resuming from an exception handler can never be sure thatthe code after the point of throw was written to deal with the executionjust continuing as if nothing had happened. An exception handler cannot knowhow much context to "get right" before resuming.To get such code right, the writer of the throw and the writer of the catchneed intimate knowledge of each others code and context. This creates acomplicated mutual dependency that wherever it has been allowed has led toserious maintenance problems.
I seriously considered the possibilityof allowing resumption when I designed the C++ exception handling mechanismand this issue was discussed in quite some detail during standardization.See the exception handling chapter ofThe Design and Evolution of C++.
If you want to check to see if you can fix a problem before throwing anexception, call a function that checks and then throws only if the problemcannot be dealt with locally. A new_handler is an example of this.
In C++, a better way of dealing with reallocation is to use a standardlibrary container, such as vector, andlet it grow naturally.
First of all there are things that just can't be done right without exceptions.Consider an error detected in a constructor; how do you report the error?You throw an exception.That's the basis ofRAII (Resource Acquisition Is Initialization),which it the basis of some of the most effective modern C++ design techniques:A constructor's job is to establish the invariant for the class(create the environment in which the members function are to run)and that often requires the acquisition of resources, such as memory, locks, files, sockets, etc.
Imagine that we did not have exceptions, how would you deal with an error detected in a constructor?Remember that constructors are often invoked initialize/construct objects in variables:
vector<double> v(100000); // needs to allocate memoryofstream os("myfile"); // needs to open a fileThevector orofstream (output file stream) constructorcould either set the variable intoa "bad" state (as ifstream does by default) so that every subsequent operation fails.That'snot ideal. For example, in the case ofofstream,your output simply disappears if you forget to check that theopen operation succeeded. For most classes that results are worse.At least, we would have to write:vector<double> v(100000); // needs to allocate memoryif (v.bad()) { /* handle error */ }// vector doesn't actually have a bad(); it relies on exceptionsofstream os("myfile"); // needs to open a fileif (os.bad()) { /* handle error */ }That's an extra test per object (to write, to remember or forget).This gets really messy for classes composed of several objects, especially if those sub-objectsdepend on each other.For more information seeThe C++ Programming Languagesection 8.3, Chapter 14, andAppendix Eor the (more academic) paperException safety: Concepts and techniques.So writing constructors can be tricky without exceptions, but what about plain old functions?We can either return an error code or set a non-local variable (e.g.errno).Setting a global variable doesn't work too well unless you test it immediately(or some other function might have re-set it).Don't even think of that technique if you might have multiple threads accessingthe global variable.The trouble with return values are that choosing the error return valuecan require cleverness and can be impossible:
double d = my_sqrt(-1);// return -1 in case of errorif (d == -1) { /* handle error */ }int x = my_negate(INT_MIN);// Duh?There is no possible value formy_negate() to return:Every possibleint is the correct answer for someintand there is no correct answer for the most negative number in the twos-complement representation.In such cases, we would need to return pairs of values (and as usual remember to test)See myBeginning programming bookfor more examples and explanations.Common objections to the use of exceptions:
In C++, exceptions are used to signal errors that cannot be handled locally,such as the failure to acquire a resource in a constructor. For example:
class Vector {int sz;int* elem;class Range_error { };public:Vector(int s) : sz(s) { if (sz<0) throw Range_error(); /* ... */ }// ...};Do not use exceptions as simply another way to return a value from a function.Most users assume - as the language definition encourages them to - that exception-handlingcode is error-handling code,and implementations are optimized to reflect that assumption.A key technique isresource acquisiton is initialization (sometimes abbreviated to RAII),which uses classes with destructors to impose order on resource management.For example:
void fct(string s){File_handle f(s,"r");// File_handle's constructor opens the file called "s"// use f} // here File_handle's destructor closes the fileIf the "use f" part of fct() throws an exception, the destructor is still invoked andthe file is properly closed.This contrasts to the common unsafe usage:void old_fct(const char* s){FILE* f = fopen(s,"r");// open the file named "s"// use ffclose(f);// close the file}If the "use f" part of old_fct throws an exception - or simply does a return - the file isn'tclosed. In C programs, longjmp() is an additional hazard.class Apple : public Fruit { void apple_fct(); /* ... */ };class Orange : public Fruit { /* ... */ }; // Orange doesn't have apple_fct()vector<Apple*> v;// vector of Applesvoid f(vector<Fruit*>& vf)// innocent Fruit manipulating function{vf.push_back(new Orange);// add orange to vector of fruit}void h(){f(v);// error: cannot pass a vector<Apple*> as a vector<Fruit*>for (int i=0; i<v.size(); ++i) v[i]->apple_fct();}Had the call f(v) been legal, we would have had an Orange pretending to be an Apple.An alternative language design decision would have been to allow the unsafe conversion,but rely on dynamicchecking. That would have required a run-time check for each access to v's members, and h()would have had to throw an exception upon encountering the last element of v.
#include<iostream>#include<string>using namespace std;int main(){cout<< "Please enter a word:\n";string s;cin>>s;cout<< "You entered "<< s<< '\n';}Note that there is no explicit memory management and no fixed-sized bufferthat you could possibly overflow.If you really need a whole line (and not just a single word) you can do this:
#include<iostream>#include<string>using namespace std;int main(){cout<< "Please enter a line:\n";string s;getline(cin,s);cout<< "You entered "<< s<< '\n';}For a brief introduction to standard library facilities, such as iostream andstring, see Chaper 3 ofTC++PL3 (available online).For a detailed comparison of simple uses of C and C++ I/O, see "Learning Standard C++ as a New Language", which you can download from mypublications listTemplates supports generic programming, template metaprogramming, etc. through a combinationof features such as integer template arguments, specialization, and uniform treatment of built-inand user-defined types. The result is flexibility, generality, and performance unmatched by"generics". The STL is the prime example.
A less desirable result of the flexibility is late detection of errors and horrendouslybad error messages. This is currently being addressed indirectly withconstraints classes.
There is a caveat: Exceptions can't be used for some hard-real time projects.For example, seethe JSF air vehicle C++ coding standards.
class File_handle {FILE* p;public:File_handle(const char* n, const char* a){ p = fopen(n,a); if (p) throw Open_error(errno); }File_handle(FILE* pp){ p = pp; if (p) throw Open_error(errno); }~File_handle() { fclose(p); }operator FILE*() { return p; }// ...};void f(const char* fn){File_handle f(fn,"rw");// open fn for reading and writing// use file through f}In a system, we need a "resource handle" class for each resource. However,we don't have to have an "finally" clause for each acquisition of a resource.In realistic systems, there are far more resource acquisitions than kindsof resources, so the "resource acquisition is initialization" technique leadsto less code than use of a "finally" construct.Also, have a look at the examples of resource management inAppendix E ofThe C++ Programming Language.
#include<memory>using namespace std;struct X {int m;// ..};void f(){auto_ptr<X> p(new X);X* q = new X;p->m++;// use p just like a pointerq->m++;// ...delete q;}If an exception is thrown in the ... part, the object held by p is correctlydeleted by auto_ptr's destructor while the X pointed to by q is leaked.See TC++PL 14.4.2 for details.Auto_ptr is a very lightweight class. In particular, it is *not* a referencecounted pointer. If you "copy" one auto_ptr into another, the assigned toauto_ptr holds the pointer and the assigned auto_ptr holds 0.For example:
#include<memory>#include<iostream>using namespace std;struct X {int m;// ..};int main(){auto_ptr<X> p(new X);auto_ptr<X> q(p);cout<< "p "<< p.get()<< " q "<< q.get()<< "\n";}should print a 0 pointer followed by a non-0 pointer. For example:p 0x0 q 0x378d0auto_ptr::get() returns the held pointer.
This "move semantics" differs from the usual "copy semantics", and can besurprising. In particular, never use anauto_ptr as a member of a standardcontainer. The standard containers require the usual copy semantics.For example:
std::vector<auto_ptr<X> >v;// error
An auto_ptr holds a pointer to an individual element, not a pointer to an array:
void f(int n){auto_ptr<X> p(new X[n]);// error// ...}This is an error because the destructor will delete the pointer usingdeleterather thandelete[] and will fail to invoke the destructor for the last n-1Xs.So should we use an auto_array to hold arrays? No. There is no auto_array.The reason is that there isn't a need for one. A better solution is to useavector:
void f(int n){vector<X> v(n);// ...}Should an exception occur in the ... part,v's destructor will be correctlyinvoked.InC++11 use aUnique_ptr instead ofauto_ptr.
In particular,throw is not simply an alternative way of returning a valuefrom a function (similar toreturn).Doing so will be slow and will confuse most C++ programmers used to seing exceptions used only forerror handling.Similarly,throw isnot a good way of getting out of a loop.
class Circle : public Shape {public:Circle(Point c, int r);// no default constructor// ...};class X {public:X();// default constructor// ...};void f(int n){void* p1 = malloc(40);// allocate 40 (uninitialized) bytesint* p2 = new int[10];// allocate 10 uninitialized intsint* p3 = new int(10);// allocate 1 int initialized to 10int* p4 = new int();// allocate 1 int initialized to 0int* p4 = new int;// allocate 1 uninitialized intCircle* pc1 = new Circle(Point(0,0),10); // allocate a Circle constructed // with the specified argumentCircle* pc2 = new Circle;// error no default constructorX* px1 = new X;// allocate a default constructed X X* px2 = new X();// allocate a default constructed X X* px2 = new X[10];// allocate 10 default constructed Xs // ...}Note that when you specify a initializer using the "(value)" notation,you get initialization with that value.Unfortunately, you cannot specify that for an array.Often, avector is a better alternative to a free-store-allocated array(e.g., consider exception safety).Whenever you usemalloc() you must consider initialization and convertionof the return pointer toa proper type. You will also have to consider if you got the number of bytes right for your use.There is no performance difference betweenmalloc() andnew when you takeinitialization into account.
malloc() reports memory exhaustion by returning 0.new reports allocation and initialization errors by throwing exceptions.
Objects created bynew are destroyed bydelete.Areas of memory allocated bymalloc() are deallocated byfree().
No, in the sense that you cannot allocate an object with malloc() and free itusing delete. Nor can you allocate with new and delete with free() or userealloc() on an array allocated by new.
The C++ operators new and delete guarantee proper construction and destruction;where constructors or destructors need to be invoked, they are. The C-stylefunctions malloc(), calloc(), free(), and realloc() doesn't ensure that.Furthermore, there is no guarantee that the mechanism used by new and deleteto acquire and release raw memory is compatible with malloc() and free().If mixing styles works on your system, you were simply "lucky" - for now.
If you feel the need for realloc() - and many do - then considerusing a standard library vector.For example
// read words from input into a vector of strings:vector<string> words;string s;while (cin>>s && s!=".") words.push_back(s);The vector expands as needed.
See also the examples and discussion in "Learning Standard C++ as a NewLanguage", which you can download from mypublications list.
In C, you can implicitly convert a void* to a T*. This is unsafe. Consider:
#include<stdio.h>int main(){char i = 0;char j = 0;char* p = &i;void* q = p;int* pp = q;/* unsafe, legal C, not C++ */printf("%d %d\n",i,j);*pp = -1;/* overwrite memory starting at &i */printf("%d %d\n",i,j);}The effects of using a T* that doesn't point to a T can be disastrous.Consequently, in C++, to get a T* from a void* you need an explicit cast.For example, to get the undesirable effects of the program above, you have towrite:int* pp = (int*)q;or, using a new style cast to make the unchecked type conversion operation more visible:
int* pp = static_cast<int*>(q);Casts are best avoided.
One of the most common uses of this unsafe conversion in C is to assign theresult of malloc() to a suitable pointer. For example:
int* p = malloc(sizeof(int));In C++, use the typesafe new operator:
int* p = new int;Incidentally, the new operator offers additional advantages over malloc():
typedef std::complex<double> cmplx;/* C style: */cmplx* p = (cmplx*)malloc(sizeof(int));/* error: wrong size *//* forgot to test for p==0 */if (*p == 7) { /* ... */ }/* oops: forgot to initialize *p */// C++ style:cmplx* q = new cmplx(1,2); // will throw bad_alloc if memory is exhaustedif (*q == 7) { /* ... */ }If you want a constant that you can use in a constant expression, say asan array bound, you have two choices:
class X {static const int c1 = 7;enum { c2 = 19 };char v1[c1];char v2[c2];// ...};At first glance, the declaration of c1 seems cleaner, but note that to usethat in-class initialization syntax, the constant must be a static const ofintegral or enumeration type initialized by a constant expression.That's quite restrictive:class Y {const int c3 = 7;// error: not staticstatic int c4 = 7;// error: not conststatic const float c5 = 7;// error: not integral};I tend to use the "enum trick" because it's portable and doesn't tempt meto use non-standard extensions of the in-class initialization syntax.So why do these inconvenient restrictions exist?A class is typically declared in a header file and a header file is typicallyincluded into many translation units.However, to avoid complicated linker rules, C++ requires that every objecthas a unique definition.That rule would be broken if C++ allowed in-class definition of entitiesthat needed to be stored in memory as objects.SeeD&E for an explanation of C++'s design tradeoffs.
You have more flexibility if the const isn't needed for use in a constantexpression:
class Z {static char* p;// initialize in definitionconst int i;// initialize in constructorpublic:Z(int ii) :i(ii) { }};char* Z::p = "hello, there";You can take the address of a static member if (and only if) it has anout-of-class definition:class AE {// ...public:static const int c6 = 7;static const int c7 = 31;};const int AE::c7;// definitionint f(){const int* p1 = &AE::c6;// error: c6 not an lvalueconst int* p2 = &AE::c7;// ok// ...}delete p;// ...delete p;If the ... part doesn't touch p then the second "delete p;" is a serious errorthat a C++ implementation cannot effectively protect itself against (withoutunusual precautions).Since deleting a zero pointer is harmless by definition, a simple solutionwould be for "delete p;" to do a "p=0;" after it has done whatever elseis required.However, C++ doesn't guarantee that.
One reason is that the operand of delete need not be an lvalue. Consider:
delete p+1;delete f(x);Here, the implementation of delete does not have a pointer to which it canassign zero.These examples may be rare, but they do imply that it is not possible toguarantee that ``any pointer to a deleted object is 0.''A simpler way of bypassing that ``rule'' is to have two pointers to anobject:
T* p = new T;T* q = p;delete p;delete q;// ouch!C++ explicitly allows an implementation of delete to zero out an lvalueoperand, and Ihad hoped that implementations would do that, but that idea doesn'tseem to have become popular with implementers.
If you consider zeroing out pointers important, consider using a destroyfunction:
template<class T> inline void destroy(T*& p) { delete p; p = 0; }Consider this yet-another reason to minimize explicit use ofnew and delete by relying on standard library containers, handles, etc.
Note that passing the pointer as a reference (to allow the pointer to bezero'd out) has the added benefit of preventing destroy() from being calledfor an rvalue:
int* f();int* p;// ...destroy(f());// error: trying to pass an rvalue by non-const referencedestroy(p+1);// error: trying to pass an rvalue by non-const reference
void f(){X* p = new X;// use p}That is, there was some (mistaken) assumption that the object created by"new" would be destroyed at the end of a function.Basically, you should only use "new" if you want an object to live beyondthe lifetime of the scope you create it in. That done, you need to use"delete" to destroy it. For example:
X* g(int i) { /* ... */ return new X(i); }// the X outlives the call of g()void h(int i){X* p = g(i);// ...delete p;}If you want an object to live in a scope only, don't use "new" but simplydefine a variable: { ClassName x; // use x }The variable is implicitly destroyed at the end of the scope.Code that creates an object using new and then deletes it at the end of the same scopeis ugly, error-prone, and inefficient.For example:
void fct()// ugly, error-prone, and inefficient{X* p = new X;// use pdelete p;}void main() { /* ... */ }is not and never has been C++, nor has it even been C.See the ISO C++ standard 3.6.1[2] or the ISO C standard 5.1.2.2.1.A conforming implementation acceptsint main() { /* ... */ }andint main(int argc, char* argv[]) { /* ... */ }A conforming implementation may provide more versions of main(),but they must all have return type int.The int returned by main() is a way for a program to return a valueto "the system" that invokes it. On systems that doesn't provide such afacility the return value is ignored, but that doesn't make "void main()"legal C++ or legal C.Even if your compiler accepts "void main()" avoid it, or risk being consideredignorant by C and C++ programmers.In C++, main() need not contain an explicit return statement. In that case, thevalue returned is 0, meaning successful execution.For example:
#include<iostream>int main(){std::cout<< "This program returns the integer value 0\n";}Note also that neither ISO C++ nor C99 allows you to leave the type out of adeclaration. That is, in contrast to C89 and ARM C++ ,"int" is not assumedwhere a type is missing in a declaration.Consequently:#include<iostream>main() { /* ... */ }is an error because the return type of main() is missing.. (dot) :: ?: sizeofThere is no fundamental reason to disallow overloading of ?:.I just didn't see the need to introduce the special case of overloadinga ternary operator.Note that a function overloadingexpr1?expr2:expr3 would not be able toguarantee that only one ofexpr2 andexpr3 was executed.
Sizeof cannot be overloaded because built-in operations, such as incrementinga pointer into an array implicitly depends on it. Consider:
X a[10];X* p = &a[3];X* q = &a[3];p++;//p points to a[4]//thus the integer value of p must be//sizeof(X) larger than the integer value of qThus, sizeof(X) could not be given a new and different meaning by theprogrammer without violating basic language rules.
In N::m neither N nor m are expressions with values; N and m are names knownto the compiler and :: performs a (compile time) scope resolution ratherthan an expression evaluation. One could imagine allowing overloading of x::ywhere x is an object rather than a namespace or a class, but that would - contrary tofirst appearances - involve introducing new syntax (to allowexpr::expr).It is not obvious what benefits such a complication would bring.
Operator . (dot) could in principle be overloaded using the same technique asused for ->.However, doing so can lead to questions about whether an operation ismeant for the object overloading . or an object referred to by . For example:
class Y {public:void f();// ...};class X {//assume that you can overload .Y* p;Y& operator.() { return *p; }void f();// ...};void g(X& x){x.f();// X::f or Y::f or error?}This problem can be solved in several ways.At the time of standardization, it was not obvious which way would be best.For more details, seeD&E.
It's not a language-technical problem.Even when I first considerd it in 1983, I knew how it could be implemented.However, my experience has been that when we go beyond the most trivial examplespeople seem to have subtlely different opinions of "the obvious" meaning of usesof an operator. A classical example is a**b**c. Assume that ** has been made to meanexponentiation. Now should a**b**c mean (a**b)**c or a**(b**c)?I thought the answer was obvious and my friends agreed - and then we found that wedidn't agree on which resolution was the obvious one.My conjecture is that such problems would lead to subtle bugs.
#include<iostream>#include<string>#include<sstream>using namespace std;string itos(int i)// convert int to string{stringstream s;s<< i;return s.str();}int main(){int i = 127;string ss = itos(i);const char* p = ss.c_str();cout<< ss<< " "<< p<< "\n";}Naturally, this technique works for converting any type that you can outputusing<< to a string.For a description of string streams, see 21.5.3 ofThe C++ Programming Language.// C++ codeextern "C" void f(int);// one wayextern "C" {// another wayint g(double);double h();};void code(int i, double d){f(i);int ii = g(d);double dd = h();// ...}The definitions of the functions may look like this:/* C code: */void f(int i){/* ... */}int g(double d){/* ... */}double h(){/* ... */}Note that C++ type rules, not C rules, are used. So you can't call functiondeclared ``extern "C"'' with the wrong number of argument. For example:// C++ codevoid more_code(int i, double d){double dd = h(i,d);// error: unexpected arguments// ...}// C++ code:extern "C" void f(int);void f(int i){// ...}Now f() can be used like this:/* C code: */void f(int);void cc(int i){f(i);/* ... */}Naturally, this works only for non-member functions. If you want to callmember functions (incl. virtual functions) from C, you need to provide asimple wrapper.For example:// C++ code:class C {// ...virtual double f(int);};extern "C" double call_C_f(C* p, int i)// wrapper function{return p->f(i);}Now C::f() can be used like this:/* C code: */double call_C_f(struct C* p, int i);void ccc(struct C* p, int i){double d = call_C_f(p,i);/* ... */}If you want to call overloaded functions from C, you must provide wrapperswith distinct names for the C code to use.For example:// C++ code:void f(int);void f(double);extern "C" void f_i(int i) { f(i); }extern "C" void f_d(double d) { f(d); }Now the f() functions can be used like this:/* C code: */void f_i(int);void f_d(double);void cccc(int i,double d){f_i(i);f_d(d);/* ... */}Note that these techniques can be used to call a C++ library from C code evenif you cannot (or do not want to) modify the C++ headers.The choice between ``int* p;'' and ``int *p;'' is not about right andwrong,but about style and emphasis.C emphasized expressions; declarations wereoften considered little more than a necessary evil. C++, on the otherhand, has a heavy emphasis on types.
A ``typical C programmer'' writes ``int *p;'' and explains it ``*p iswhat is the int'' emphasizing syntax, and may point tothe C (and C++) declaration grammar to argue for the correctness of thestyle. Indeed, the * binds to the name p in the grammar.
A ``typical C++ programmer'' writes ``int* p;'' and explains it ``p isa pointer to an int'' emphasizing type. Indeed the type of p is int*.I clearly prefer that emphasisand see it as important for using the more advanced parts of C++ well.
The critical confusion comes (only) when people try to declare severalpointers with a single declaration:
int* p, p1;// probable error: p1 is not an int*Placing the * closer to the name does not make this kind of errorsignificantly less likely.
int *p, p1;// probable error?Declaring one name per declaration minimizes theproblem - in particular when we initialize the variables. People arefar less likely to write:
int* p = &i;int p1 = p;// error: int initialized by int*And if they do, the compiler will complain.
Whenever something can be done in two ways, someone will be confused.Whenever something is a matter of taste, discussions can drag onforever. Stick to one pointer per declaration and always initializevariablesand the source of confusion disappears.See The Design and Evolution of C++for a longer discussion of the C declaration syntax.
I personally use what is often called "K&R" style. When you add conventionsfor constructs not found in C, that becomes what is sometimes called"Stroustrup" style.For example:
class C : public B {public:// ...};void f(int* p, int max){if (p) {// ...}for (int i = 0; i<max; ++i) {// ...}}This style conserves vertical space better than most layout styles,and I like tofit as much as is reasonable onto a screen. Placing the opening brace of afunction on a new linehelps me distinguish function definition from class definitionsat a glance.Indentation is very important.
Design issues, such as the use ofabstract classes for major interfaces,use of templates to present flexible type-safe abstractions, andproper use of exceptions to represent errors,are far more important than the choice of layout style.
So, I don't like naming a variable after its type; what do I like and recommend?Name a variable (function, type, whatever) based on what it is or does.Choose meaningful name; that is, choose names that will help people understand your program. Evenyou will have problems understanding what your program is supposed to do if you litter it with variables with easy-to-type names like x1, x2, s3, and p7. Abbreviations and acronyms can confuse people, so use them sparingly. Acronyms shouldbe used sparingly. Consider, mtbf, TLA, myw, RTFM, and NBV. They are obvious,but wait a few months and even I will have forgotten at least one.
Short names, such as x and i, are meaningful when used conventionally; that is, x should be a local variable or a parameterand i should be a loop index.
Don't use overly long names; they are hard to type, make lines so long that they don't fit on a screen, and are hard to read quickly. These are probably ok:
partial_sum element_count staple_partitionThese are probably too long:
the_number_of_elements remaining_free_slots_in_symbol_tableI prefer to use underscores to separate words in an identifier (e.g, element_count) rather than alternatives,such as elementCount and ElementCount.Never use names with all capital letter (e.g., BEGIN_TRANSACTION) because that's conventionally reserved for macros.Even if you don't use macros, someone might have littered your header files with them.Use an initial capital letter for types (e.g., Square and Graph).The C++ language and standard library don't use capital letters, so it's int rather than Int and string rather than String.That way, you can recognize the standard types.
Avoid names that are easy to mistype, misread, or confuse. For example
name names nameSfoo f00fl f1 fI fiThe characters 0, o, O, 1, l, and I are particularly prone to cause trouble.
Often, your choice of naming conventions is limited by local style rules.Remember that a maintaining a consistent style is often more important thandoing every little detail in the way you think best.
const int a = 1;// okint const b = 2;// also okMy guess is that using the first version will confuse fewer programmers(``is more idiomatic'').
Why? When I invented "const" (initially named "readonly" and had acorresponding "writeonly"), I allowed it to go before or after the typebecause I could do so without ambiguity. Pre-standard C and C++ imposed few(if any) ordering rules on specifiers.
I don't remember any deep thoughts or involved discussions aboutthe order at the time. A few of the early users - notably me - simplyliked the look of
const int c = 10;better than
int const c = 10;at the time.
I may have been influenced by the fact that my earliest exampleswere written using "readonly" and
readonly int c = 10;does read better than
int readonly c = 10;The earliest (C or C++) code using "const" appears to have beencreated (by me) by a global substitution of "const" for "readonly".
I remember discussing syntax alternatives with several people -incl. Dennis Ritchie - but I don't remember which languages I looked at then.
Note that in const pointers, "const" always comes after the "*". For example:
int *const p1 = q;// constant pointer to int variableint const* p2 = q;// pointer to constant intconst int* p3 = q;// pointer to constant int
x = (T)y;We don't know. It depends on the type T and the types of x and y.T could be the name of a class, a typedef, or maybe a template parameter.Maybe x and y are scalar variables and (T) represents a value conversion.Maybe x is of a class derived from y's class and (T) is a downcast.Maybe x and y are unrelated pointer types.Because the C-style cast (T) can be used to express many logically different operations,the compiler has only the barest chance to catch misuses.For the same reason, a programmer may not know exactly what a cast does. This is sometimesconsidered an advantage by novice programmers and is a source of subtle errors when thenovice guessed wrong.
The "new-style casts" were introduced to give programmers a chance to state their intentionsmore clearly and for the compiler to catch more errors. For example:
int a = 7;double* p1 = (double*) &a;// ok (but a is not a double)double* p2 = static_cast<double*>(&a);// errordouble* p2 = reinterpret_cast<double*>(&a);// ok: I really mean itconst int c = 7;int* q1 = &c;// errorint* q2 = (int*)&c;// ok (but *q2=2; is still invalid code and may fail)int* q3 = static_cast<int*>(&c);// error: static_cast doesn't cast away constint* q4 = const_cast<int*>(&c);// I really mean itThe idea is that conversions allowed by static_cast are somewhat less likely to lead to errorsthan those that require reinterpret_cast.In principle, it is possible to use the result of a static_cast without casting it back to itsoriginal type, whereas you should always cast the result of a reinterpret_cast back to its originaltype before using it to ensure portability.
A secondary reason for introducing the new-style cast was that C-style casts are very hardto spot in a program. For example, you can't conveniently search for casts using an ordinaryeditor or word processor. This near-invisibility of C-style casts is especially unfortunatebecause they are so potentially damaging. An ugly operation should have an ugly syntactic form.That observation was part of the reason for choosing the syntax for the new-style casts.A further reason was for the new-style casts to match the template notation, so that programmerscan write their own casts, especially run-time checked casts.
Maybe, because static_cast is so ugly and so relatively hard to type, you're more likelyto think twice before using one? That would be good, because casts really are mostly avoidablein modern C++.
Consider:
#include "someheader.h"struct S {int alpha;int beta;};If someone (unwisely) has written a macro called "alpha" or a macro called"beta" this may not compile or (worse) compile into something unexpected.For example, "someheader.h" may contain:#define alpha 'a'#define beta b[2]Conventions such as having macros (and only macros) in ALLCAPS helps,but there is no language-level protection against macros.For example, the factthat the member names were in the scope of the struct didn't help:Macros operate ona program as a stream of characters before the compiler proper sees it.This, incidentally, is a major reason why C and C++ program development environments and tools have been unsophisticated: the human and the compilersee different things.
Unfortunately, you cannot assume that other programmers consistentlyavoid what youconsider "really stupid". For example, someone recently reported to me thatthey had encountered a macro containing a goto. I have seen that also andheard arguments that might - in a weak moment - appear to make sense.For example:
#define prefix get_ready(); int ret__#define Return(i) ret__=i; do_something(); goto exit#define suffix exit: cleanup(); return ret__int f(){prefix;// ...Return(10);// ...Return(x++);//...suffix;}Imagine being presented with that as a maintenance programmer;"hiding" the macros in a header - as is not uncommon - makes this kind of"magic" harder to spot.One of the most common subtle problems is that a function-style macro doesn'tobey the rules of function argument passing. For example:
#define square(x) (x*x)void f(double d, int i){square(d);// finesquare(i++);// ouch: means (i++*i++)square(d+1);// ouch: means (d+1*d+1); that is, (d+d+1)// ...}The "d+1" problem is solved by adding parentheses in the "call" or in themacro definition:#define square(x) ((x)*(x))/* better */However, the problem with the (presumably unintended) double evaluation of i++remains.
And yes, I do know that there are things known as macros that doesn't sufferthe problems of C/C++ preprocessor macros.However, I have no ambitions for improving C++ macros. Instead, I recommend the use of facilities from the C++ language proper,such as inline functions, templates, constructors (for initialization),destructors (for cleanup), exceptions (for exiting contexts), etc.