Со времён С++98 стандартные контейнеры и идиома RAII позволяли избегать использования оператора delete,
что делало код более безопасным. С приходом С++11 и умных указателей отпала необходимость использовать оператор new, что позволило практически полностью переложить управление памятью на плечи компилятора. В
докладе объясняется идеология управления памятью и ресурсами в современном С++.
%in Midrand+277-882-255-28 abortion pills for sale in midrand
С++ without new and delete
1. C++ without new and delete
by Mikhail Matrosov
mikhail.matrosov@gmail.com
mmatrosov@aligntech.com
2. Instead of contents
• What?
– Avoid new and delete in modern C++
• Why?
– new and delete increase complexity
• How?
– std::make_shared, std::make_unique
– Containers (STL, boost, libcds, etc.; or your own)
– Wrappers for third-party smart pointers
– Wrappers for libraries with specific memory management
C++ Russia, Moscow, 2015 2
3. Modern С++
• Modern С++ includes
– С++14 features
– Modern best practices
• C++14 is officially released
– ISO/IEC 14882:2014 / December 15, 2014
– Fully or partially supported by all major compilers
C++ Russia, Moscow, 2015 3
4. What & how?
Managing memory and objects from C to modern C++
C++ Russia, Moscow, 2015 4
5. Objects in С
typedef struct ObjectTag
{
// Object members
} Object;
int InitObject(Object* io_pObject);
int UseObject(Object* io_pObject);
int ClearObject(Object* io_pObject);
void ObjectsInC()
{
Object* pObject;
pObject = malloc(sizeof(Object)); // Allocate memory
InitObject(pObject); // Establish invariants and acquire resources
UseObject(pObject); // Do the work
ClearObject(pObject); // Release resources
free(pObject); // Release memory
// By the way: error handling :-/
}
C++ Russia, Moscow, 2015 5
6. Objects in С++
• Encapsulation:
– Constructors
– Destructors
– Methods
class Object
{
// Object members
public:
Object(); // Establish invariants and acquire resources
~Object(); // Release resources
void Use(); // Do the work
};
• Error handling:
– Exceptions
C++ Russia, Moscow, 2015 6
7. new
• Allocate memory
• Initialize object
in constructor
delete
• Deinitialize object
in destructor
• Free memory
C++ Russia, Moscow, 2015 7
new creates a single object on the heap
– Usually to share the object
new[] creates a number of objects on the heap
– Usually when number is known at runtime or is big enough
8. Objects in С++
// 1. Naive C++ style
Object* p = new Object();
p->Use();
delete p;
// 2. More secure C++98 with RAII
std::auto_ptr<Object> ap(new Object());
ap->Use();
// 3. More secure C++11 with RAII
std::unique_ptr<Object> up(new Object());
up->Use();
// 4. New bullet-proof modern C++
auto up2 = std::make_unique<Object>();
up2->Use();
C++ Russia, Moscow, 2015 8
9. Shared objects in С++
void UseObject(Object*); // Shall only observe the object
void AcceptObject(Object*); // Shall delete the object
void ShareObject(std::shared_ptr<Object>); // May do both!
// Naive C++
Object* p = new Object();
p->Use();
UseObject(p); // Preserve ownership
p->Use();
AcceptObject(p); // Transfer ownership
// C++98 & RAII
/* No way standard to say this! */
// Modern C++
auto sp = std::make_shared<Object>();
sp->Use();
ShareObject(sp); // Share
sp->Use();
ShareObject(std::move(sp)); // Share and discard ownership
C++ Russia, Moscow, 2015 9
10. Reference counting objects in С++
// Naive C++ (not used)
RefCounted* p = new RefCounted();
p->AddRef();
p->Use();
p->Release();
// C++98 & RAII
RefPtr<RefCounted> rp = new RefCounted();
rp->Use();
// Modern C++
auto rp2 = MakeRefPtr<RefCounted>();
rp2->Use();
C++ Russia, Moscow, 2015 10
11. Dynamic arrays in С++
void UseArray(int* ptr, int count);
int n = 100;
// Naive C++
int* p = new int[n];
UseArray(p, n);
delete[] p;
// C++98 & RAII
std::vector<int> v(n);
UseArray(&v[0], v.size());
// Modern C++
std::vector<int> v2(n);
UseArray(v2.data(), v.size());
C++ Russia, Moscow, 2015 11
13. Better == simple
• A bit of authority:
– Software's Primary Technical Imperative: Managing Complexity
[McConnel2004]
– Make Simple Tasks Simple! [Stroustrup2014]
– ≈99.9998% developers are not experts [Sutter2014]
– KISS principle (Keep It Simple, Stupid)
• What is simple?
– Keep simple things simple
– Don’t make complex things unnecessarily complex
– Write for clarity and correctness first, optimize later
• How to keep it simple?
– DRY principle (Don’t Repeat Yourself)
– “By default” principle [Sutter2014]
C++ Russia, Moscow, 2015 13
14. “By default” principle
• The main point:
– Common task ⇔ common solution
• Advantages:
– Mutual understanding with other developers
• Programs are parallelized in development-time too!
– Easy for beginners
– Less to think – productive laziness
– Same as for coding conventions
• See also “A tour of C++” [Stroustrup2013]
C++ Russia, Moscow, 2015 14
15. std::make_unique<T>() vs.
std::unique_ptr(new T)
int GenerateId();
std::pair<std::unique_ptr<Object>, int> MakeObjectWithIdWrong()
{
return std::make_pair(
std::unique_ptr<Object>(new Object()),
GenerateId());
}
std::pair<std::unique_ptr<Object>, int> MakeObjectWithIdRight()
{
return std::make_pair(
std::make_unique<Object>(), // Safe
GenerateId());
}
DRY!
C++ Russia, Moscow, 2015 15
// May throw!
Exactly the same for std::shared_ptr
and std::make_shared!
// May leak!
18. Advantages of make functions
• Both:
– Exception safe
– No type name duplication
– Symmetry
• std::make_shared:
– Single memory allocation
C++ Russia, Moscow, 2015 18
20. When not to use make functions
• Both:
– Cannot specify custom deleters
– Cannot pass braced initializer
• std::unique_ptr:
– Cannot specify custom allocator (for std::shared_ptr there is
std::allocate_shared function) [see N3588, section IV.4]
• std::shared_ptr:
– Potential memory waste for long-living weak pointers
– Works poorly with class-specific operators new&delete
– Potential false sharing of the object and the reference counting
block [see question on StackOverflow]
• See also “Effective modern C++”, Item 21 [Meyers2014]
C++ Russia, Moscow, 2015 20
21. Summary
• By default, raw delete is not used in C++ since RAII
and standard containers
• By default, raw new is not used in modern C++ within
the standard library
• If either is used, situation is not default and requires
special handling (and, probably, special attention)
C++ Russia, Moscow, 2015 21
23. Memory management techniques
• No memory management
– Explicit new and delete
– Not used in good code
• Non-intrusive smart pointers
– Applicable for all objects
– Standard library
• Intrusive smart pointers
– Reference counting objects
– Third-party libraries
• Other
– Parent to child relationships, coupling with system resources
– Qt, MFC
C++ Russia, Moscow, 2015 23
24. Reference counting objects
• OpenSceneGraph
– Open source 3D framework written in C++ and OpenGL
– osg::Referenced – base reference counting class
• ref() increments counter
• unref() decrements counter and deletes the object when counter
drops to zero
– osg::ref_ptr<T> – smart pointer
• Calls ref() on construction
• Calls unref() on destruction
C++ Russia, Moscow, 2015 24
25. OSG: C++ & RAII
• Official code sample from [Wang2010, p. 78]
• Perfect fit for upgrade
– Can be done automatically
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
// Init vertices
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
// Init normals
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->setVertexArray(vertices.get());
geom->setNormalArray(normals.get());
// Further init geometry and use it
C++ Russia, Moscow, 2015 25
26. OSG: Modern C++
namespace osg
{
template<typename T, typename... Args>
osg::ref_ptr<T> make_ref(Args&&... args)
{
return new T(std::forward<Args>(args)...);
}
}
auto vertices = osg::make_ref<osg::Vec3Array>();
// Init vertices
auto normals = osg::make_ref<osg::Vec3Array>();
// Init normals
auto geom = osg::make_ref<osg::Geometry>();
geom->setVertexArray(vertices.get());
geom->setNormalArray(normals.get());
// Further init geometry and use it
C++ Russia, Moscow, 2015 26
27. Other memory management
• MFC (Microsoft Foundation Class)
– C++ wrapper classes for Windows API
– CWnd – base class for all windows objects
• Represents both a C++ object and an HWND
– C++ objects are allocated in the application's heap
– HWNDs are allocated in system resources
• A set of rules that prevent system resource or memory leaks
– Requires new and delete this for modeless dialogs (see
TN017: Destroying Window Objects)
C++ Russia, Moscow, 2015 27
28. Modeless dialogs in MFC
C++ Russia, Moscow, 2015 28
void CMainFrame::OnBnClickedCreate()
{
CMyDialog* pDialog = new CMyDialog;
pDialog->Create(IDD_MY_DIALOG, this);
pDialog->ShowWindow(SW_SHOW);
}
class CMyDialog : public CDialog
{
// ...
protected:
void CMyDialog::PostNcDestroy() override
{
CDialog::PostNcDestroy();
delete this;
}
// ...
};
29. Other memory management
• Qt
– Cross-platform application and UI framework
– QObject – base class, managing child-parent relationships
• Has a list of children, deletes them on destruction
• Has a pointer to parent, which may be null
• May change parent during the lifetime
– Requires explicit new and delete by design (see discussion)
C++ Russia, Moscow, 2015 29
30. Summary
• Same default rules for reference counting objects
• May require dedicated wrappers for some libraries,
like in MFC
• May not be applicable for specific memory
management techniques, like in Qt
C++ Russia, Moscow, 2015 30
31. Conclusion
• Good code is simple code
• “By default” principle to simplify things
• By default, raw delete is not used in C++ since it was
initially standardized
• Avoiding raw new works good in standard library;
declare this approach a default – for simplification
• If either new or delete is used, we assume that
– Situation requires special handling (and attention)
– Or specific memory management technique is used
C++ Russia, Moscow, 2015 31
33. Bibliography
[McConnell2004] McConnell, Steve, and Detlef Johannis. Code
complete. Vol. 2. Redmond: Microsoft press, 2004.
[Wang2010] Wang, Rui, and Xuelei Qian. OpenSceneGraph 3.0:
Beginner's guide. Packt Publishing Ltd, 2010.
[Lavavej2012] Lavavej, Stephan. STL11: Magic && Secrets. Going
Native, 2012.
[Stroustrup2013] Stroustrup, Bjarne. A Tour of C++. Addison-Wesley,
2013.
[Stroustrup2014] Stroustrup, Bjarne. Make Simple Tasks Simple!
CppCon, 2014.
[Sutter2014] Sutter, Herb. Back to the Basics! Essentials of Modern C++
Style. CppCon, 2014.
[Meyers2014] Meyers, Scott. Effective Modern C++: 42 Specific Ways
to Improve Your Use of C++ 11 and C++ 14. "O'Reilly Media, Inc.", 2014.
C++ Russia, Moscow, 2015 33
34. QObject
• Use cases:
– Object is created on the stack
• Deleted once went out of scope
– Object is created in the heap with a parent
• Deleted by the parent
– Object is created in the heap without a parent
• Need to delete manually
• Or need to specify a parent manually
Матросов Михаил, С++ без new и delete,
Russian C++ User Group, Саратов, 2014
34
35. Qt::MakeChild
• Wrapper for safe use case
• Now new goes for unsafe case
• Drawback: cannot delay parent assignment
namespace Qt
{
template<class T, class... Args>
T* MakeChild(Args&&... args)
{
T* pObject = new T(std::forward<Args>(args)...);
Q_ASSERT(pObject->parent() != nullptr);
return pObject;
}
}
Матросов Михаил, С++ без new и delete,
Russian C++ User Group, Саратов, 2014
35
36. Qt::MakeChild
MyWidget::MyWidget()
{
// Safe, created on the stack
QProgressDialog progress("In progress...", "Cancel", 0, 100);
// Safe, parent is specified
{
// Regular, valid since C++98
QPushButton* pButton = new QPushButton("Push me", this);
// Proposed, modern C++ style
auto pButton2 = Qt::MakeChild<QPushButton>("And me", this);
}
// Unsafe, parent is null, need manual delete
m_pDialog = new QDialog();
}
Матросов Михаил, С++ без new и delete,
Russian C++ User Group, Саратов, 2014
36
37. CModelessDialog
C++ Russia, Moscow, 2015 37
class CModelessDialog : public CDialog {
protected:
using CDialog::CDialog;
void CModelessDialog::PostNcDestroy() override {
CDialog::PostNcDestroy();
delete this;
}
};
class CMyDialog : public CModelessDialog {
// ...
public:
static CMyDialog* CreateModeless(CWnd* pParent = nullptr) {
auto* pDialog = new CMyDialog;
pDialog->Create(IDD_MY_DIALOG, pParent);
pDialog->ShowWindow(SW_SHOW);
return pDialog;
}
protected:
CMyDialog() = default;
// ...
};