In the last installment of this blog, I introduced the Factory Method: Creational Patterns: Factory Method 1. My implementation had two serious issues: slicing and ownership semantics. Today, I fix these issues.
To remind you, here is a simplified and slightly modified implementation of the Factory Method in my last post. First, this implementation support only the member function
clone; second, also the base class
Window should be cloneable.
The program produces the expected polymorphic behavior.
First: What is slicing?
- Slicing means you want to copy an object during assignment or initialization, and you get only a part of the object.
Slicing is one of the darkest corners of C++. Let me give you a simple example:
The expressions (1), (2), and (3) have all the same effect: The
Derived part of
d is removed. I assume that was not your intention.
You may ask yourself. Why is slicing an issue for the example factoryMethodWindowIssues.cpp. Let me quote the C++ Core Guidelines: C.67: A polymorphic class should suppress public copy/move.
Okay, there is a new question: What is a polymorphic class?
- A polymorphic class is a class that defines or inherits at least one virtual function.
Here is the issue.
Window in the program factoryMethodWindowIssues.cpp is a polymorphic class, and it does not suppress public copy/move. A
Window can be a victim of slicing. The following program exemplifies this. I just removed the reference from the function signature of the function
Due to the fact that the factory function
cloneWindow takes its argument by copy and not by reference anymore, slicing kicks in.
Let’s see what happens when I follow rule C.67: A polymorphic class should suppress public copy/move.
Here is the fixed program:
I deleted the copy constructor (line 1) and copy assignment operator (line 2). Due to the declared copy constructor, the class
Window supports no move semantics. Additionally, the default constructor is also not declared. Therefore, I have to default it (line 1).
The error message of the Microsoft Visual C++ compiler comes directly to the point:
The necessary copy constructor is deleted.
Okay. Let me write about the ownership issue of the program.
In general, you don’t know the implementation of the factory function
cloneWindow returns a pointer to
Window. Pointers have, by design, a flaw. They model two completely different semantics: ownership and borrowing.
- Ownership: the caller is responsible for the Windows and must destroy it; this is the behavior that program
- Borrowing: the callee is responsible for the Window and borrows it from the caller
Let me emphasize this one more.
- Owner: You are the owner of the
Window. You have to take care of it and destroy it. If not, you have a memory leak.
- Borrower: You are not the owner of the Window. You can not destroy it. If you destroy it, you have a double delete.
How can we overcome this design flaw? The rescue has two parts. A weak one based on discipline and a strong one based on the type system.
The Weak Rescue
The weak rescue is based on discipline: In modern C++, we don’t transfer ownership with a raw pointer.
The Strong Rescue
The strong rescue is based on the type system. When you want to transfer ownership, use a smart pointer. You have two choices:
std::unique_ptr<Window>: Returning a
std::unique_ptr<Window>means that the caller is the owner. The
std::unique_ptr<Window>behaves such as a local. When it goes out of scope, it is automatically destroyed.
std::shared_ptr<Window>: Returning a
std::shared_ptr<Window>means that the caller and the called share ownership. When neither the caller nor the callee needs the
std::shared_ptr<Window>anymore, it is automatically destroyed.
The following program
factoryMethodUniquePtr.cpp uses a
std::unique_ptr<Window> to explicitly transfer ownership. Additionally, a
std::unique_ptr can not be copied. Consequentially, the factory function creates a new
Finally, here is the output of the program.
create member function returns a
std::unique_ptr<Window>, but creates under the hood a
std::unique_ptr<DefaultWindow>, or a
std::unique_ptr<FancyWindow>. Therefore, the polymorphic behavior of the
createWindow function is preserved.
Additionally, this implementation based on
std::unique_ptr solves the slicing issue because a
std::unique_ptr<Window> cannot be copied.
The final program
factoryMethodUniquePtr.cpp is fine. It overcomes the slicing and ownership issues. Let me elaborate in my next post on the most controversial pattern of the Design Patterns book: the Singleton pattern.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, and Bhavith C Achar.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
|My special thanks to Embarcadero|
|My special thanks to PVS-Studio|
|My special thanks to Tipi.build|
|My special thanks to Take Up Code|
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,