C++ Topics

Beginner: Level 1 [Refer Textbook]
  1. Some Basics
  2. Classes and Objects
  3. Constructors and Destructors
  4. Operator Overloading
  5. Inheritance
  6. Polymorphism, Pointers and Virtual functions
  7. Templates
  8. Exception Handling
Advanced:  Level2

References:
  1. Constructors/Destructors (in depth)
  2. Storage class and Linkage
  3. Overloading and Overriding
  4. Pure Virtual Functions
  5. RTTI
  6. Conversions
  7. Exceptions
  8. Operator Overloading
  9. Auto Pointers
  10. Templates
  11. Name Look-up and Using Declarations
  12. Shallow Copy and Deep Copy
  13. Virtual Table
  14. Miscellaneous Topics

Static and dynamic libraries

A library is a package of code that is meant to be reused by many programs. Typically, a C++ library comes in two pieces:
1) A header file that defines the functionality the library is exposing (offering) to the programs using it.
2) A precompiled binary that contains the implementation of that functionality pre-compiled into machine language.
Some libraries may be split into multiple files and/or have multiple header files.
Libraries are precompiled for several reasons. First, since libraries rarely change, they do not need to be recompiled often. It would be a waste of time to recompile the library every time you wrote a program that used them. Second, because precompiled objects are in machine language, it prevents people from accessing or changing the source code, which is important to businesses or people who don’t want to make their source code available for intellectual property reasons.

There are two types of libraries: static libraries and dynamic libraries.
     A static library (also known as an archive) consists of routines that are compiled and linked directly into your program. When you compile a program that uses a static library, all the functionality of the static library becomes part of your executable. On Windows, static libraries typically have a .lib extension, whereas on linux, static libraries typically have an .a (archive) extension. One advantage of static libraries is that you only have to distribute the executable in order for users to run your program. Because the library becomes part of your program, this ensures that the right version of the library is always used with your program. Also, because static libraries become part of your program, you can use them just like functionality you’ve written for your own program. On the downside, because a copy of the library becomes part of every executable that uses it, this can cause a lot of wasted space. Static libraries also can not be upgraded easy — to update the library, the entire executable needs to be replaced.

     A dynamic library (also called a shared library) consists of routines that are loaded into your application at run time. When you compile a program that uses a dynamic library, the library does not become part of your executable — it remains as a separate unit. On Windows, dynamic libraries typically have a .dll (dynamic link library) extension, whereas on Linux, dynamic libraries typically have a .so (shared object) extension. One advantage of dynamic libraries is that many programs can share one copy, which saves space. Perhaps a bigger advantage is that the dynamic library can be upgraded to a newer version without replacing all of the executables that use it.
Because dynamic libraries are not linked into your program, programs using dynamic libraries must explicitly load and interface with the dynamic library. This mechanisms can be confusing, and makes interfacing with a dynamic library awkward. To make dynamic libraries easier to use, an import library can be used.

    An import library is a library that automates the process of loading and using a dynamic library. On Windows, this is typically done via a small static library (.lib) of the same name as the dynamic library (.dll). The static library is linked into the program at compile time, and then the functionality of the dynamic library can effectively be used as if it were a static library. On Linux, the shared object (.so) file doubles as both a dynamic library and an import library.


Copy constructor and Assignment operator

Although using the assignment operator is fairly straightforward, correctly implementing an overloaded assignment operator can be a little more tricky than you might anticipate. There are two primary reasons for this.
First, there are some cases where the assignment operator isn’t called when you might expect it to be.
Second, there are some issues in dealing with dynamically allocated memory (which we will cover in the next lesson).
The assignment operator is used to copy the values from one object to another already existing object. The key words here “already exist”.
Consider the following example:
Cents cMark(5); // calls Cents constructor
Cents cNancy; // calls Cents default constructor
cNancy = cMark; // calls Cents assignment operator
In this case, cNancy has already been created by the time the assignment is executed. Consequently, the Cents assignment operator is called. The assignment operator must be overloaded as a member function.
What happens if the object being copied into does not already exist? To understand what happens in that case, we need to talk about the copy constructor.

The copy constructor
Consider the following example:
Cents cMark(5); // calls Cents constructor
Cents cNancy = cMark; // calls Cents copy constructor!
Because the second statement uses an equals symbol in it, you might expect that it calls the assignment operator. However, it doesn’t! It actually calls a special type of constructor called a copy constructor. A copy constructor is a special constructor that initializes a new object from an existing object.
The purpose of the copy constructor and the assignment operator are almost equivalent — both copy one object to another. However, the assignment operator copies to existing objects, and the copy constructor copies to newly created objects.
The difference between the copy constructor and the assignment operator causes a lot of confusion for new programmers, but it’s really not all that difficult. Summarizing:
  • If a new object has to be created before the copying can occur, the copy constructor is used.
  • If a new object does not have to be created before the copying can occur, the assignment operator is used.
There are three general cases where the copy constructor is called instead of the assignment operator:
   1. When instantiating one object and initializing it with values from another object (as in the example above).
   2. When passing an object by value.
   3. When an object is returned from a function by value.
In each of these cases, a new variable needs to be created before the values can be copied — hence the use of the copy constructor.
Because the copy constructor and assignment operator essentially do the same job (they are just called in different cases), the code needed to implement them is almost identical.
An overloaded assignment operator and copy constructor example
Now that you understand the difference between the copy constructor and assignment operator, let’s see how they are implemented. For simple classes such as our Cents class, it is very straightforward.
Here is a simplified version of our Cents class:
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents=0)
{
m_nCents = nCents;
}
};
First, let’s add the copy constructor. Thinking about this logically, because it is a constructor, it needs to be named Cents. Because it needs to copy an existing object, it needs to take a Cents object as a parameter. And finally, because it is a constructor, it doesn’t have a return type. Putting all of these things together, here is our Cents class with a copy constructor.
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents=0)
{
m_nCents = nCents;
}
// Copy constructor
Cents(const Cents &cSource)
{
m_nCents = cSource.m_nCents;
}
};
A copy constructor looks just like a normal constructor that takes a parameter of the class type. However, there are two things which are worth explicitly mentioning. First, because our copy constructor is a member of Cents, and our parameter is a Cents, we can directly access the internal private data of our parameter. Second, the parameter MUST be passed by reference, and not by value. Can you figure out why?

 The answer lies above in the list that shows the cases where a copy constructor is called. A copy constructor is called when a parameter is passed by value. If we pass our cSource parameter by value, it would need to call the copy constructor to do so. But calling the copy constructor again would mean the parameter is passed by value again, requiring another call to the copy constructor. This would result in an infinite recursion (well, until the stack memory ran out and the the program crashed).
Fortunately, modern C++ compilers will produce an error if you try to do this:
C:\\Test.cpp(431) : error C2652: 'Cents' : illegal copy constructor: first parameter must not be a 'Cents'
The first parameter in this case must be a reference to a Cents!
Now let’s overload the assignment operator. Following the same logic, the prototype and implementation are fairly straightforward:
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents=0)
{
m_nCents = nCents;
}
 
// Copy constructor
Cents(const Cents &cSource)
{
m_nCents = cSource.m_nCents;
}
 
     Cents& operator= (const Cents &cSource);
 
 };
 
 Cents& Cents::operator= (const Cents &cSource)
 {
     // do the copy
     m_nCents = cSource.m_nCents;
 
     // return the existing object
     return *this;
 }
A couple of things to note here: First, the line that does the copying is exactly identical to the one in the copy constructor. This is typical. In order to reduce duplicate code, the portion of the code that does the actual copying could be moved to a private member function that the copy constructor and overloaded assignment operator both call. Second, we’re returning *this so we can chain multiple assigments together:
cMark = cNancy = cFred = cJoe; // assign cJoe to everyone
If you need a refresher on chaining, we cover that in the section on overloading the I/O operators.
Finally, note that it is possible in C++ to do a self-assignment:
cMark = cMark; // valid assignment
In these cases, the assignment operator doesn’t need to do anything (and if the class uses dynamic memory, it can be dangerous if it does). It is a good idea to do a check for self-assignment at the top of an overloaded assignment operator.
Here is an example of how to do that:

Cents& Cents::operator= (const Cents &cSource)
 {
     // check for self-assignment by comparing the address of the
     // implicit object and the parameter
     if (this == &cSource)
         return *this;
 
     // do the copy
     m_nCents = cSource.m_nCents;
 
     // return the existing object
     return *this;
 }
Note that there is no need to check for self-assignment in a copy-constructor. This is because the copy constructor is only called when new objects are being constructed, and there is no way to assign a newly created object to itself in a way that calls to copy constructor.

Default member-wise copying
Just like other constructors, C++ will provide a default copy constructor if you do not provide one yourself. However, unlike other operators, C++ will provide a default assignment operator if you do not provide one yourself!
Because C++ does not know much about your class, the default copy constructor and default assignment operators it provides are very simple. They use a copying method known as a memberwise copy (also known as a shallow copy).
Source: