-
Notifications
You must be signed in to change notification settings - Fork 15
CPP Notes
From http://lemire.me/blog/archives/2012/06/20/do-not-waste-time-with-stl-vectors/ One STL option is:
vector<double> array(N) // pre-allocate
// push back code goes here
Classic C style in C++:
double *array = new double[N];
STL vectors with reserve
:
vector<double> array;
array.reserve(N)
push_back
with out preallocation is slowest. Classic C is fastest, then followed by pre-allocation. Note Pre-allocation uses extra cycles on memory initialization to zero.
By default, cin
is synced with stdio, slows things down. Use this:
cin.sync_with_stdio(false)
There's more info on this Stackoverflow question.
auto_ptr
is deprecated; use unique_ptr
(no copying allowed, except through std::move
from utilities
) or shared_ptr
(reference counted, so deconstruct when number of references is 0; copying allowed). Good (though slightly dated) write up at Smart Pointers - What, Why, Which?.
Smart pointers are a natural choice in containers with different classes, as mentioned in the discussion above. A container templated for a base class cannot store derived class objects (as containers store by value), so some sort of pointer would be needed to store objects of different class. Standard pointers would require a cleanup, e.g.:
for (vector<SomeClass*>::iterator it = x.begin(), it != x.end(); ++it) {
delete *it;
}
This is a lot of work, and is not not exception safe, so smart pointers are preferred.
Some neat subtle points about storing pointers in STL containers.
First, as mentioned above, storing regular "dumb" pointers can be very harmful. They can create both dangling pointers or memory leaks. Consider:
std::vector<SomeClass*> x;
x.push_back(new SomeClass());
x.pop_back(); // note: delete not called! This creates a memory leak!
// or
x.push_back(new SomeClass());
delete x.back(); // without pop_back after, this creates a dangling pointer!
auto_ptr
s are deprecated in C++11, so I won't discuss their limitations as a smart pointer for containers. See the C++ FAQ or this StackOverflow Question for more information. A better solution is the shared_ptr
:
typedef shared_ptr<SomeClass> SomeClassPtr;
std::vector<SomeClassPtr> x;
Note that as containers that handle dynamic memory allocation, there's a lot of copying of objects that could occur behind the scenes, as part of the container implementation. shared_ptr
allows for this. There's a nice story about this issue in Dr. Dobbs too.
Here's an interesting gotcha... auto_ptr
(less of a concern, since they're deprecated; may apply for unique_ptr
s) and shared_ptr
s use delete
in their destructors, not delete[]
. Consequently, using these with dynamically allocated arrays will not work as expected:
std::shared_ptr<std::string> stringy(new std::string[10]);
// leave scope, we have memory leak
This is discussed in Scott Meyer's great book Effective C++ (item 13). Note that Boost has work arounds. Note that Meyers will warns against releasing resources manually with delete is bad: "This Item's guidance to use objects to manage resources suggests that if you're releasing resources manually (e.g. using delete
other than in a resource-managing class), you're doing something wrong." He advocates the use of resource-managing classes to do this.
Another subtle point about resource management and shared pointers: return shared pointers by value, not references. Consider:
shared_ptr<SomeClass> MakeSmartClass() {
return shared_ptr<SomeClass> (new SmartClass());
}
If we instead returned a reference to SmartClass
, it's not guaranteed the reference counting mechanism of the shared_ptr
would be updated (I believe since the copy constructor is not called). This can create problems.
Passing locals by reference is just bad (as it is in C). This is stupid:
int &NumberGetter() {
int i = 23; // this won't exist outside of this function body
return i;
}
This is discussed in this StackOverflow thread.
Usually people fall in the pitfall of thinking if I save extra copying calls when I pass an argument in a function by reference, won't I get some efficiency gains by returning references? Scott Meyers talks about this in Item 21. There's also a terrific article on this topic on C++Next Want Speed? Pass by Value.
Why is it safe to pass by value? Copy elision
Pimpl is an C++ idiom in which the implementation of a class is separated from the interface. The implementation is instead an implementation class, pointed at with an opaque pointer — a pointer at the implementation or pimpl
. Herb Sutter introduces and expands on it. In design patterns, this is related to the bridge pattern. A large advantage of Pimpl is that when the implementation changes, all code using the interface class does not need to be recompiled. In contrast, without this interface/implementation separation, all modules uses a changed class would need to be recompiled.
Some interesting discussion on forward declarations on StackOverflow.
In general, all Clang compilations should use -Wextra
. See this excellent post on programmers.stackexchange.com by a Google Clang developer.
As a matter of being explicit (and avoiding Clang's unused parameter
warning when compiling with -Wextra
), it's good to static cast ignored returns or parameters to void. For example:
int f();
void foo(int some_param) {
static_cast<void> (f()); // explicitly ignore return
static_cast<void> (some_param); // explicitly ignore parameter
// do stuff
}
Stroustrup has a great example of how templates and types can be used to solve problems. The Mars Climate Orbiter crashed due to inconsistencies in units in calculations. Stroustrup solves this problem using types in this article.
See this page for an example.
Stroustrup's talk on C++11 Style.
Get max of a type T
in a template with std::numeric_limits<T>::max();
(see [http://www.cplusplus.com/reference/limits/numeric_limits/](numeric limits)). See also type_traits
— compile time type checking.
Possible with static
members and list initilization, e.g. id(static_next_id++)
. But, to solve linking issues:
http://stackoverflow.com/questions/17857882/how-i-can-auto-increment-each-class-objects
Header file inclusion cycle screw ups, mosty likely. See: http://stackoverflow.com/questions/18903300/c-unknown-type-name-my-structure