What are the often misunderstood concepts in c++?
C++ or C/C++? I would say for both the most misunderstood parts are memory management and pointers. The former because people don't free appropriately (too early or not at all!) and the latter because a lot of people just don't "get" pointers (either in the pass by reference "guise" or in general in something like a linked list).
In decreasing order:
- make sure to release pointers for allocated memory
- when destructors should be virtual
- how virtual functions work
Interestingly not many people know the full details of virtual functions, but still seem to be ok with getting work done.
Pointers.
Dereferencing the pointers. Through either .
or ->
Address of using &
for when a pointer is required.
Functions that take params by reference by specifing a &
in the signature.
Pointer to pointers to pointers ***
or pointers by reference void someFunc(int *& arg)
Here are some:
- Using templates to implement polymorphism without vtables, à la ATL.
- Logical
const
-ness vs actualconst
-ness in memory. When to use themutable
keyword.
ACKNOWLEDGEMENT: Thanks for correcting my mistake, spoulson.
EDIT:
Here are more:
- Virtual inheritance (not virtual methods): In fact, I don't understand it at all! (by that, I mean I don't know how it's implemented)
- Unions whose members are objects whose respective classes have non-trivial constructors.
The static keyword which can mean one of three distinct things depending on where it is used.
- It can be a static member function or member variable.
- It can be a static variable or function declared at namespace scope.
- It can be a static variable declared inside a function.
Here is an important concept in C++ that is often forgotten:
C++ should not be simply used like an object oriented language such as Java or C#. Inspire yourself from the STL and write generic code.
C++ is not C with classes!
And there is no language called C/C++. Everything goes downhill from there.
Very nice resource I can't get tired to promote - C++ Frequently Questioned Answers
The difference between assignment and initialisation:
string s = "foo"; // initialisation
s = "bar"; // assignment
Initialisation always uses constructors, assignment always uses operator=
There are a few things that people seem to be constantly confused by or have no idea about:
Pointers, especially function pointers and multiple pointers (e.g. int(*)(void*), void***)
The const keyword and const correctness (e.g. what is the difference between const char*, char* const and const char* const, and what does void class::member() const; mean?)
Memory allocation (e.g. every pointer new'ed should be deleted, malloc/free should not be mixed with new/delete, when to use delete [] instead of delete, why the C functions are still useful (e.g. expand(), realloc()))
Scope (i.e. that you can use { } on its own to create a new scope for variable names, rather than just as part of if, for etc...)
Switch statements. (e.g. not understanding that they can optimise as well (or better in some cases) than chains of ifs, not understanding fall through and its practical applications (loop unrolling as an example) or that there is a default case)
Calling conventions (e.g. what is the difference between cdecl and stdcall, how would you implement a pascal function, why does it even matter?)
Inheritance and multiple inheritance and, more generally, the entire OO paradigm.
Inline assembler, as it is usually implemented, is not part of C++.
a classic among beginners to c++ from c:
confuse delete
and delete[]
EDIT:
another classic failure among all levels of experience when using C API:
std::string helloString = "hello world";
printf("%s\n", helloString);
instead of:
printf("%s\n", helloString.c_str());
it happens to me every week. You could use streams, but sometimes you have to deal with printf-like APIs.
The overuse of inheritance unrelated to polymorphism. Most of the time, unless you really do use runtime polymorphism, composition or static polymorphism (i.e., templates) is better.
A big one is that the languages are not 100% syntactically compatible. C++ is fully link compatible with C, but some C++ style syntax will generate a complier error in C. Some compilers aren't picky and don't follow the C standard to the letter. The basics of these need to be learned when moving from one language to the other. Note I haven't read the latest C standard, but last I knew this was true.
- Pointers to members and pointers to member functions.
- Non-type template parameters.
- Multiple inheritance, particularly virtual base classes and shared base objects.
- Order of construction and destruction, the state of virtual functions in the middle of constructing an intermediate base class.
- Cast safety and variable sizes. No, you can't assume that
sizeof(void *) == sizeof(int)
(or any other type for that matter, unless a portable header specifically guarantees it) in portable code. - Pointer arithmetic.
- That anonymous namespaces are almost always what is truly wanted when people are making static variables in C++
- When making library header files, the pimpl idiom (http://www.gotw.ca/gotw/024.htm) should be used for almost all private functions and members to aid in dependency management
The most pernicious concept I've seen is that it should be treated as C with some addons. In fact, with modern C++ systems, it should be treated as a different language, and most of the C++-bashing I see is based on the "C with add-ons" model.
To mention some issues:
While you probably need to know the difference between delete
and delete[]
, you should normally be writing neither. Use smart pointers and std::vector<>
.
In fact, you should be using a *
only rarely. Use std::string for strings. (Yes, it's badly designed. Use it anyway.)
RAII means you don't generally have to write clean-up code. Clean-up code is bad style, and destroys conceptual locality. As a bonus, using RAII (including smart pointers) gives you a lot of basic exception safety for free. Overall, it's much better than garbage collection in some ways.
In general, class data members shouldn't be directly visible, either by being public
or by having getters and setters. There are exceptions (such as x and y in a point class), but they are exceptions, and should be considered as such.
And the big one: there is no such language as C/C++. It is possible to write programs that can compile properly under either language, but such programs are not good C++ and are not normally good C. The languages have been diverging since Stroustrup started working on "C with Classes", and are less similar now than ever. Using "C/C++" as a language name is prima facie evidence that the user doesn't know what he or she is talking about. C++, properly used, is no more like C than Java or C# are.
Given this:
int x = sizeof(char);
what value is X?
The answer you often hear is dependant on the level of understanding of the specification.
- Beginner - x is one because chars are always eight bit values.
- Intermediate - it depends on the compiler implementation, chars could be UTF16 format.
- Expert - x is one and always will be one since a char is the smallest addressable unit of memory and sizeof determines the number of units of memory required to store an instance of the type. So in a system where a char is eight bits, a 32 bit value will have a sizeof of 4; but in a system where a char is 16 bits, a 32 bit value will have a sizeof of 2.
It's unfortunate that the standard uses 'byte' to refer to a unit of memory since many programmers think of 'byte' as being eight bits.
Skizz
- The difference between pointer (*) and reference (&)
- Accesing multi dimentional arrays using pointers.
- Using * to acess the value of a pointer - i.e. *ptr = 5
- When to release allocated memory.
That C++ does have automatic resource management.
(Most people who claim that C++ does not have memory management try to use new and delete way too much, not realising that if they allowed C++ to manage the resource themselves, the task gets much easier).
Example: (Made with a made up API because I do not have time to check the docs now)
// C++
void DoSomething()
{
File file("/tmp/dosomething", "rb");
... do stuff with file...
// file is automatically free'ed and closed.
}
// C#
public void DoSomething()
{
File file = new File("/tmp/dosomething", "rb");
... do stuff with file...
// file is NOT automatically closed.
// What if the caller calls DoSomething() in a tight loop?
// C# requires you to be aware of the implementation of the File class
// and forces you to accommodate, thus voiding implementation-hiding
// principles.
// Approaches may include:
// 1) Utilizing the IDisposable pattern.
// 2) Utilizing try-finally guards, which quickly gets messy.
// 3) The nagging doubt that you've forgotten something /somewhere/ in your
// 1 million loc project.
// 4) The realization that point #3 can not be fixed by fixing the File
// class.
}
Arrays are not pointers
They are different. So &array
is not a pointer to a pointer, but a pointer to an array. This is the most misunderstood concept in both C and C++ in my opinion. You gotta have a visit to all those SO answers that tell to pass 2-d arrays as type**
!
C++ is a multi-paradigm language. Many people associate C++ strictly with OOP.
Free functions are not bad just because they are not within a class C++ is not an OOP language alone, but builds upon a whole stack of techniques.
I've heard it many times when people say free functions (those in namespaces and global namespace) are a "relict of C times" and should be avoided. Quite the opposite is true. Free functions allow to decouple functions from specific classes and allow reuse of functionality. It's also recommended to use free functions instead of member functions if the function don't need access to implementation details - because this will eliminate cascading changes when one changes the implementation of a class among other advantages.
This is also reflected in the language: The range-based for loop in C++0x
(next C++ version released very soon) will be based on free function calls. It will get begin / end iterators by calling the free functions begin
and end
.
C++ is not a typical object oriented language.
Don't believe me? look at the STL, way more templates than objects.
It's almost impossible to use Java/C# ways of writing object oriented code; it simply doesn't work.
- In Java/C# programming, there's alot of
new
ing, lots of utility objects that implement some single cohesive functionality. - In C++, any object
new
ed must be deleted, but there's always the problem of who owns the object - As a result, objects tend to be created on the stack
- But when you do that, you have to copy them around all the time if you're going to pass them around to other functions/objects, thus wasting a lot of performance that is said to be achieved with the unmanaged environment of C++
- Upon realizing that, you have to think about other ways of organizing your code
- You might end up doing things the procedural way, or using metaprogramming idioms like smart pointers
- At this point, you've realized that OO in C++ cannot be used the same way as it is used in Java/C#
If you insist on doing oop with pointers, you'll usually have large (gigantic!) classes, with clearly defined ownership relationships between objects to avoid memory leaks. And then even if you do that, you're already too far from the Java/C# idiom of oop.
Actually I made up the term "object-oriented", and I can tell you I did not have C++ in mind.
-- Alan Kay (click the link, it's a video, the quote is at 10:33)
Although from a purist point of view (e.g. Alan Kay), even Java and C# fall short of true oop
Why is A[b] the same thing as b[A]?
Ok, not really a COMMON question, but it came up in a class I was teaching once...
Headers and implementation files
This is also a concept misunderstood by many. Questions like what goes into header files and why it causes link errors if function definitions appear multiple times in a program on the one side but not when class definitions appear multiple times on the other side.
Very similar to those questions is why it is important to have header guards.
std::vector
does not create elements when reserve is used
I've seen it that programmers argue that they can access members at positions greater than what size()
returns if they reserve()
'ed up to that positions. That's a wrong assumption but is very common among programmers - especially because it's quite hard for the compiler to diagnose a mistake, which will silently make things "work".
A pointer is an iterator, but an iterator is not always a pointer
This is also an often misunderstood concept. A pointer to an object is a random access iterator: It can be incremented/decremented by an arbitrary amount of elements and can be read and written. However, an iterator class that has operator overloads doing that fulfill those requirements too. So it is also an iterator but is of course not a pointer.
I remember one of my past C++ teachers was teaching (wrongly) that you get a pointer to an element of a vector if you do vec.begin()
. He was actually assuming - without knowing - that the vector implements its iterators using pointers.
If a function accepts a pointer to a pointer, void*
will still do it
I've seen that the concept of a void pointer is frequently confused. It's believed that if you have a pointer, you use a void*
, and if you have a pointer to a pointer, you use a void**
. But you can and should in both cases use void*
. A void**
does not have the special properties that a void*
has.
It's the special property that a void*
can also be assigned a pointer to a pointer and when cast back the original value is received.
I still don't get why vector doesn't have a pop_front and the fact that I can't sort(list.begin(), list.end())..
I think the most misunderstood concept about C++ is why it exists and what its purpose is. Its often under fire from above (Java, C# etc.) and from below (C). C++ has the ability to operate close to the machine to deal with computational complexity and abstraction mechanisms to manage domain complexity.
I know this is old question but I thought object slicing / failure of polymorphism with objects on the stack worth mentioning. If I do not use C++ for six months, this one always comes out and bites me when I use the language again.
#include <iostream>
class base {
public:
base(int val) { var1 = val; };
virtual void doSomething() { var1 *= 2; };
int getVar1() { return var1; };
virtual ~base() { };
protected:
int var1;
};
class deriv : public base {
public:
deriv(int val) : base(val) { };
void doSomething() { var1 *= 4; };
};
void use_object_ptr(base *arg) {
arg->doSomething();
std::cout << arg->getVar1() << std::endl;
};
void use_object_copy(base arg) {
arg.doSomething();
std::cout << arg.getVar1() << std::endl;
};
int main(int argc, char **argv) {
deriv d(42);
deriv *p2d = new deriv(42);
use_object_ptr(p2d); // calls deriv::doSomething(), prints 168
use_object_copy(d); // calls base::doSomething(), prints 84
use_object_ptr(&d); // calls deriv::doSomething(), prints 168
return 0;
}
NULL
is always zero.
Many confuse NULL
with an address, and think therefor it's not necessarily zero if the platform has a different null pointer address.
But NULL
is always zero and it is not an address. It's an zero constant integer expression that can be converted to pointer types.