Finally finished making my way through Lesson 9 in Sam’s Teach Yourself C++ in One Hour a Day, 8th Edition
Some of the concepts I don’t fully understand just yet, but I’m hoping that StackOverflow may help with these.
For the remaining topics listed here, I’m going to try my best to explain them simply, in my own words.
Just as a variable is copied when it is passed as a parameter (by value) to a function, objects are copied, as well.
Problems may arise if this default copy behavior is allowed with objects. For instance, if you dynamically create a pointer in the constructor, when this object is copied the pointer is also copied. This means that you now have two pointers which point to the same address.
Now, when the destructor for one object is executed (presuming you do delete dynamically allocated memory), the address which the pointer was pointing to is no longer valid. When the second object’s (the copied object) destructor executes and attempts to delete the pointer, it crashed since that address is no longer valid. When an object is copied in this way, it is known as a shallow copy.
In these cases, a copy constructor is needed, which results in a deep copy. See the syntax below.
In the copy constructor, the object itself is passed to the constructor by reference. Now, in contexts where a copy occurs, you can be sure that it is a deep copy, meaning that any pointers would point to a different address than the source.
Note: Along with the copy constructor you would want to implement the same deep copy for the assignment operator. This would cover any contexts where objects are being assigned, which result in a copy instruction.
A move constructor is another part of “move semantics,” along with the Copy Constructor and Assignment Operator. From my understanding, the move constructor, when defined is used by the compiler automatically to help with performance, specifically cases where a double copy might occur. Rather than performing two deep copy operations, the compiler will opt to literally move the data once.
The syntax is:
Generally, these 3 operations (deep copy, assignment operators, move constructor) are known as “move semantics.” Apparently, these are really only necessary when using raw pointers. I believe in modern C++, with STL and smart pointers, there is no need for these. However, it is useful to know for legacy code, or if you absolutely need to use raw pointers.
There is a short course on move semantics on Lynda that I will go through soon to get some more understanding. I’m sure I’ll write a post on it as I go through it.
Prevent Object Copy
There may be instances where you do not want to allow an object to be copied. For instance, in an OS you may want to model only one processor or one LAN. You would not want these resources to be copied or duplicated.
To prevent an object from being copied or duplicated, you can make private the copy constructor and assignment operator. It is sufficient to merely declare them.
A singleton is a design pattern where only one instance of a class is created/permitted. For example, if you had a class President, realistically you would only want to allow one instance of this class to be created, since there can only be one president at one time.
To accomplish this in C++, you would need to make use of private constructors and private assignment operators to prevent copying/duplication, as mentioned above. Additionally, you would need a static instance member.
By creating a static method, you ensure that it is shared across all instances of that class. Now, when you use static on a local variable inside a function, this ensures that the variables retains its value in between function calls.
The combination of private constructors, assignment operators and static methods restricts the class to one instance. Any additional calls to GetInstance() will only return the one available object that was already created.
Prevent Object Instance On Stack
In some cases a class may contain a lot of data that you would not want loaded on the stack. For one, you may have a stack overflow, or you are simply wasting cycles on loading/unloading a lot of data at once from the stack.
To prevent an object from being instantiated on the stack, and instead have it instantiated in free store, you need to declare the destructor as private.
Now, when you attempt to create an object by instantiating it on the stack:
It will result in a compile error, since the compiler is looking for a destructor to automatically call when the object goes out of scope. Since the destructor is private, it will not be found by main(), so the program cannot compile.
To successfully compile, you will need to instantiate the object in free store:
hugeObject* o1 = new hugeObject();
Using this syntax you do not need a destructor to compile. However, it is best to call the destructor in order to avoid memory leaks. In this case, you will need to define a public static method that deletes the object (class methods are able to access private members).
Struct vs. Class
In C++ a struct is a construct that is treated similarly to a class. There are a couple key differences.
- In a struct members are
publicby default (
privatefor a class).
- Unless specified, a struct features
publicinheritance from a base struct (
Declaring a Friend of a Class
By using the keyword
friend with a class or function, you can allow external access to private members and functions of a class.
In this example, the private method, DisplayAge, is marked with the keyword friend. As a result, it can be accessed in global scope (in main function). Not only can it call the function, DisplayAge, it can access the private member, age.
As mentioned above, the friend keyword can also be used with classes (i.e classes can be considered friends of other classes). This results in friend classes being given access to private members and methods of other classes.
According to this chapter: “A union is a special class type where only one of the non-static data members is active at a time.”
Basic declaration and instantiation of a union:
By assigning a value to member2, you are making it the active member of the union.
Where to use a union
Apparently, a union is often used within a struct to form a complex data type.
According to this book: “This combination of a struct comprising an enumeration to hold type information and a union to hold value is a popular application of the union.”
Note: There is only a brief description in this book, so I know I’ll have to dig into this further.