VTK/Tutorials/SmartPointers: Difference between revisions

From KitwarePublic
< VTK‎ | Tutorials
Jump to navigationJump to search
(Removed the pitfalls section, the dangling object reference was incorrect)
 
(15 intermediate revisions by the same user not shown)
Line 7: Line 7:
===Creating an Object with a Smart Pointer===
===Creating an Object with a Smart Pointer===
One way to create a VTK object is
One way to create a VTK object is
<source lang="cpp">
<div><code>vtkObject* MyObject = vtkObject::New();</code></div>
  vtkObject* MyObject = vtkObject::New();
</source>


This method, however, can (and likely will) lead to memory management issues at some point or another. You must manually delete the object
This method, however, can (and likely will) lead to memory management issues at some point or another. You must manually delete the object
<source lang="cpp">
<div><code>MyObject->Delete();</code></div>
  MyObject->Delete();
</source>
 
or you will have a memory leak. VTK's solution to this ever-annoying problem is the smart pointer. To use it, you must
or you will have a memory leak. VTK's solution to this ever-annoying problem is the smart pointer. To use it, you must
 
<div></code>
<source lang="cpp">
<nowiki>#</nowiki>include <vtkSmartPointer.h></code></div>
#include <vtkSmartPointer.h>
</source>


Then you can create an object as follows:
Then you can create an object as follows:
<source lang="cpp">
<div><code>vtkSmartPointer<vtkObject> MyObject = vtkSmartPointer<vtkObject>::New();</code></div>
vtkSmartPointer<vtkObject> MyObject = vtkSmartPointer<vtkObject>::New();
</source>


Note the special syntax on the right of the assignment. One almost never wants to assign a raw object pointer to a smart pointer, as is done here:
Note the special syntax on the right of the assignment. One almost never wants to assign a raw object pointer to a smart pointer, as is done here:
<source lang="cpp">
<div><code>vtkSmartPointer<vtkObject> MyObject = vtkObject::New();</code></div>
vtkSmartPointer<vtkObject> MyObject = vtkObject::New();
</source>


This is almost always a coding error, because the reference count is incremented, requiring an explicit Delete later.
This is almost always a coding error, because the reference count is incremented, requiring an explicit Delete later.
Line 38: Line 27:
When putting a VTK object allocated elsewhere into a Smart pointer use the TakeReference method. This passes the object's self-ownership to the smart pointer:
When putting a VTK object allocated elsewhere into a Smart pointer use the TakeReference method. This passes the object's self-ownership to the smart pointer:


<source lang="cpp">
<div><code>vtkPolyData *NewMesh()
vtkPolyData *NewMesh()
{
{
   vtkPolyData *pd = vtkPolyData::New();
   vtkPolyData *pd = vtkPolyData::New();
Line 48: Line 36:


vtkSmartPointer<vtkPolyData> MyObject;
vtkSmartPointer<vtkPolyData> MyObject;
MyObject.TakeReference(NewMesh());
MyObject.TakeReference(NewMesh());</code></div>
</source>


Because MyObject now owns the dataset, there is no need to invoke the Delete method.  
Because MyObject now owns the dataset, there is no need to invoke the Delete method.  


The Take method should be used on the right hand side of smart pointer assignments. For example:
The Take method should be used on the right hand side of smart pointer assignments. For example:
<source lang="cpp">
<div><code>vtkSmartPointer<vtkPolyData> MyObject
vtkSmartPointer<vtkPolyData> MyObject
   = vtkSmartPointer<vtkPolyData>::Take(NewMesh());</code></div>
   = vtkSmartPointer<vtkPolyData>::Take(NewMesh());
</source>


A word of caution, Take is a static member function and as such won't modify the smart pointer instance. The following illustrates this '''incorrect''' usage.
A word of caution, Take is a static member function and as such won't modify the smart pointer instance. The following illustrates this '''incorrect''' usage.


<source lang="cpp">
<div><code>vtkSmartPointer<vtkPolyData> MyObject;
vtkSmartPointer<vtkPolyData> MyObject;
MyObject.Take(NewMesh());    // WRONG!! MyObject remains empty!
MyObject.Take(NewMesh());    // WRONG!! MyObject remains empty!
MyObject->GetNumberOfCells(); // SEGV!!
MyObject->GetNumberOfCells(); // SEGV!!</code></div>
</source>


=== Getting an Object with a Smart Pointer ===
=== Getting an Object with a Smart Pointer ===
When not allocating memory for an object, you can still use smart pointers. Take this simple example:
When not allocating memory for an object, you can still use smart pointers. Take this simple example:
<source lang="cpp">
<div><code>vtkSmartPointer<vtkXMLPolyDataReader> Reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
vtkSmartPointer<vtkXMLPolyDataReader> Reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
vtkPolyData* pd = Reader->GetOutput();</code></div>
vtkPolyData* pd = Reader->GetOutput();
</source>
vs
vs
<source lang="cpp">
<div><code>vtkSmartPointer<vtkPolyData> pd = Reader->GetOutput();</code></div>
vtkSmartPointer<vtkPolyData> pd = Reader->GetOutput();
</source>


In the first case, when the reader object goes out of scope, the data is deleted. In the second case, by using a smart pointer we have incremented the data's reference count by 1, so the data will not be deleted until the reader AND the polydata object go out of scope.
In the first case, when the reader object goes out of scope, the data is deleted. In the second case, by using a smart pointer we have incremented the data's reference count by 1, so the data will not be deleted until the reader AND the polydata object go out of scope.
Line 84: Line 63:


Smart pointer parameter:
Smart pointer parameter:
<source lang="cpp">
<div><code>void MyFunction(vtkSmartPointer<vtkPolyData> polydata)
void MyFunction(vtkSmartPointer<vtkPolyData> polydata)
{
{
}
}
Line 92: Line 70:


vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
MyFunction(polydata);
MyFunction(polydata);</code></div>
</source>


Normal pointer parameter:
Normal pointer parameter:
<source lang="cpp">
<div><code>void MyFunction(vtkPolyData* polydata)
void MyFunction(vtkPolyData* polydata)
{
{
}
}
Line 104: Line 80:


vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
MyFunction(polydata.GetPointer());
MyFunction(polydata.GetPointer());</code></div>
</source>


The reverse is also true. If you have a normal pointer, you can pass it to a function that accepts either a smart pointer or a normal pointer.
The reverse is also true. If you have a normal pointer, you can pass it to a function that accepts either a smart pointer or a normal pointer.


Smart pointer parameter:
Smart pointer parameter:
<source lang="cpp">
<div><code>void MyFunction(vtkSmartPointer<vtkPolyData> polydata)
void MyFunction(vtkSmartPointer<vtkPolyData> polydata)
{
{
}
}
Line 118: Line 92:


vtkPolyData* polydata = vtkPolyData::New();
vtkPolyData* polydata = vtkPolyData::New();
MyFunction(polydata);
MyFunction(polydata);</code></div>
</source>


Normal pointer parameter:
Normal pointer parameter:
<source lang="cpp">
<div><code>void MyFunction(vtkPolyData* polydata)
void MyFunction(vtkPolyData* polydata)
{
{
}
}
Line 130: Line 102:


vtkPolyData* polydata = vtkPolyData::New();
vtkPolyData* polydata = vtkPolyData::New();
MyFunction(polydata);
MyFunction(polydata);</code></div>
</source>


=== Returning a Smart Pointer ===
=== Returning a Smart Pointer ===
Line 137: Line 108:


You should define a function like this:
You should define a function like this:
<source lang="cpp">
<div><code>vtkSmartPointer<vtkPolyData> MyFunction()
vtkSmartPointer<vtkPolyData> MyFunction()
{
{
   vtkSmartPointer<vtkPolyData> myObject = vtkSmartPointer<vtkPolyData>::New();
   vtkSmartPointer<vtkPolyData> myObject = vtkSmartPointer<vtkPolyData>::New();
   return myObject;
   return myObject;
}
}</code></div>
</source>


And call the function using:
And call the function using:
<source lang="cpp">
<div><code>vtkSmartPointer<vtkPolyData> MyPolydata = MyFunction();</code></div>
vtkSmartPointer<vtkPolyData> MyPolydata = MyFunction();
</source>


The smart pointer in the function is copied to the smart pointer in the caller, so the reference count remains unchanged and the associated object is not deleted.
The smart pointer in the function is copied to the smart pointer in the caller, so the reference count remains unchanged and the associated object is not deleted.
Line 154: Line 121:
'''Incorrect'''
'''Incorrect'''


<source lang="cpp">
<div><code>vtkPolyData* MyFunction()
vtkPolyData* MyFunction()
{
{
   vtkSmartPointer<vtkPolyData> MyObject = vtkSmartPointer<vtkPolyData>::New();
   vtkSmartPointer<vtkPolyData> MyObject = vtkSmartPointer<vtkPolyData>::New();
Line 161: Line 127:
}
}


vtkPolyData* MyPolydata = MyFunction();
vtkPolyData* MyPolydata = MyFunction();</code></div>
</source>


In this case, the smart pointer is converted to a raw pointer before being returned to the caller. As the function exits, the smart pointer's reference count goes to zero and the actual object is deleted, leaving the raw pointer dangling, pointing at freed memory.
In this case, the smart pointer is converted to a raw pointer before being returned to the caller. As the function exits, the smart pointer's reference count goes to zero and the actual object is deleted, leaving the raw pointer dangling, pointing at freed memory.
Line 170: Line 135:


Declare the pointer like this:
Declare the pointer like this:
<source lang="cpp">
<div><code>class MyClass
class MyClass
{
{
   vtkSmartPointer<vtkFloatArray> Distances;
   vtkSmartPointer<vtkFloatArray> Distances;
};
};</code></div>
</source>


You can initialize the smart pointer in your constructor using an initializer:
You can initialize the smart pointer in your constructor using an initializer:
<source lang="cpp">
<div><code>MyClass::MyClass()
MyClass::MyClass()
: Distances(vtkSmartPointer<vtkFloatArray>::New())
: Distances(vtkSmartPointer<vtkFloatArray>::New())
{}
{}</code></div>
</source>


Or you can initialize it with an assignment statement:
Or you can initialize it with an assignment statement:
<source lang="cpp">
<div><code>MyClass::MyClass()
MyClass::MyClass()
{
{
   Distances = vtkSmartPointer<vtkFloatArray>::New();
   Distances = vtkSmartPointer<vtkFloatArray>::New();
}
}</code></div>
</source>


There is no need to explicitly Delete the object in your class destructor. By using smart pointers in your classes, your destructors become much simpler. You may find that you don't have to write a destructor at all, as the default destructor will Delete all your objects through the magic of the smart pointer.
There is no need to explicitly Delete the object in your class destructor. By using smart pointers in your classes, your destructors become much simpler. You may find that you don't have to write a destructor at all, as the default destructor will Delete all your objects through the magic of the smart pointer.
Line 198: Line 157:


===SmartPointers.cpp===
===SmartPointers.cpp===
<source lang="cpp">
<div><code>#include <vtkFloatArray.h>
#include <vtkFloatArray.h>
<nowiki>#</nowiki>include <vtkSmartPointer.h>
#include <vtkSmartPointer.h>


void WithSmartPointers();
void WithSmartPointers();
Line 222: Line 180:
   vtkFloatArray* Distances = vtkFloatArray::New();
   vtkFloatArray* Distances = vtkFloatArray::New();
   Distances->Delete();
   Distances->Delete();
}
}</code></div>
</source>


===CMakeLists.txt===
===CMakeLists.txt===
<source lang="cmake">
<div><code>cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 2.6)


project(SmartPointers)
project(SmartPointers)
Line 240: Line 196:
else()
else()
   target_link_libraries(SmartPointers vtkHybrid)
   target_link_libraries(SmartPointers vtkHybrid)
endif()
endif()</code></div>
</source>

Latest revision as of 14:40, 23 April 2025

Idea

The idea behind the use of smart pointers is reference counting and more automated memory managment. If the object goes out of scope, and it is not being used anywhere else, it will be deleted automatically. This is an important part of writing exception-safe code. With the proper use of smart pointers, you should never see an explicit Delete invocation in your code. The smart pointer classes, and which one is most appropriate, has been summarized in a Source article.

Note: vtkSmartPointer in combination with the internal reference counting of vtkObject reproduces much of the functionality of the std::tr1::shared_ptr template class introduced in the C++ Standard's Library Extensions Technical Report 1 (known as TR1). (This class first appeared as part of the Boost library.) If you're familiar with shared_ptr, then you already know most of how to use vtkSmartPointer. The principle difference is that, in VTK, the reference count is stored in the object, not the smart pointer. A vtkObject "owns itself", and the special vtkSmartPointer<>::New() method creates an object and smart pointer in one step, with the pointer holding the initial ownership of the object.

Usage

Creating an Object with a Smart Pointer

One way to create a VTK object is

vtkObject* MyObject = vtkObject::New();

This method, however, can (and likely will) lead to memory management issues at some point or another. You must manually delete the object

MyObject->Delete();

or you will have a memory leak. VTK's solution to this ever-annoying problem is the smart pointer. To use it, you must

#include <vtkSmartPointer.h>

Then you can create an object as follows:

vtkSmartPointer<vtkObject> MyObject = vtkSmartPointer<vtkObject>::New();

Note the special syntax on the right of the assignment. One almost never wants to assign a raw object pointer to a smart pointer, as is done here:

vtkSmartPointer<vtkObject> MyObject = vtkObject::New();

This is almost always a coding error, because the reference count is incremented, requiring an explicit Delete later.

Putting an Existing Object into a Smart Pointer

When putting a VTK object allocated elsewhere into a Smart pointer use the TakeReference method. This passes the object's self-ownership to the smart pointer:

vtkPolyData *NewMesh()

{

 vtkPolyData *pd = vtkPolyData::New();
 // build the mesh
 // ...
 return pd;

}

vtkSmartPointer<vtkPolyData> MyObject;

MyObject.TakeReference(NewMesh());

Because MyObject now owns the dataset, there is no need to invoke the Delete method.

The Take method should be used on the right hand side of smart pointer assignments. For example:

vtkSmartPointer<vtkPolyData> MyObject = vtkSmartPointer<vtkPolyData>::Take(NewMesh());

A word of caution, Take is a static member function and as such won't modify the smart pointer instance. The following illustrates this incorrect usage.

vtkSmartPointer<vtkPolyData> MyObject;

MyObject.Take(NewMesh()); // WRONG!! MyObject remains empty!

MyObject->GetNumberOfCells(); // SEGV!!

Getting an Object with a Smart Pointer

When not allocating memory for an object, you can still use smart pointers. Take this simple example:

vtkSmartPointer<vtkXMLPolyDataReader> Reader = vtkSmartPointer<vtkXMLPolyDataReader>::New(); vtkPolyData* pd = Reader->GetOutput();

vs

vtkSmartPointer<vtkPolyData> pd = Reader->GetOutput();

In the first case, when the reader object goes out of scope, the data is deleted. In the second case, by using a smart pointer we have incremented the data's reference count by 1, so the data will not be deleted until the reader AND the polydata object go out of scope.

Passing a Smart Pointer to a function

If you have a smart pointer, you can pass it to a function that accepts either a smart pointer or a normal pointer.

Smart pointer parameter:

void MyFunction(vtkSmartPointer<vtkPolyData> polydata)

{ }

...

vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();

MyFunction(polydata);

Normal pointer parameter:

void MyFunction(vtkPolyData* polydata)

{ }

...

vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();

MyFunction(polydata.GetPointer());

The reverse is also true. If you have a normal pointer, you can pass it to a function that accepts either a smart pointer or a normal pointer.

Smart pointer parameter:

void MyFunction(vtkSmartPointer<vtkPolyData> polydata)

{ }

...

vtkPolyData* polydata = vtkPolyData::New();

MyFunction(polydata);

Normal pointer parameter:

void MyFunction(vtkPolyData* polydata)

{ }

...

vtkPolyData* polydata = vtkPolyData::New();

MyFunction(polydata);

Returning a Smart Pointer

Correct

You should define a function like this:

vtkSmartPointer<vtkPolyData> MyFunction()

{

 vtkSmartPointer<vtkPolyData> myObject = vtkSmartPointer<vtkPolyData>::New();
 return myObject;
}

And call the function using:

vtkSmartPointer<vtkPolyData> MyPolydata = MyFunction();

The smart pointer in the function is copied to the smart pointer in the caller, so the reference count remains unchanged and the associated object is not deleted.

Incorrect

vtkPolyData* MyFunction()

{

 vtkSmartPointer<vtkPolyData> MyObject = vtkSmartPointer<vtkPolyData>::New();
 return MyObject;

}

vtkPolyData* MyPolydata = MyFunction();

In this case, the smart pointer is converted to a raw pointer before being returned to the caller. As the function exits, the smart pointer's reference count goes to zero and the actual object is deleted, leaving the raw pointer dangling, pointing at freed memory.

Using Smart Pointers as Class Member Variables

Smart pointers make class destructors simple, by automatically releasing ownership of shared objects.

Declare the pointer like this:

class MyClass

{

 vtkSmartPointer<vtkFloatArray> Distances;
};

You can initialize the smart pointer in your constructor using an initializer:

MyClass::MyClass()
Distances(vtkSmartPointer<vtkFloatArray>::New())
{}

Or you can initialize it with an assignment statement:

MyClass::MyClass()

{

 Distances = vtkSmartPointer<vtkFloatArray>::New();
}

There is no need to explicitly Delete the object in your class destructor. By using smart pointers in your classes, your destructors become much simpler. You may find that you don't have to write a destructor at all, as the default destructor will Delete all your objects through the magic of the smart pointer.

Example

Here is an example of equivalent operations with and without smart pointers:

SmartPointers.cpp

#include <vtkFloatArray.h>

#include <vtkSmartPointer.h>

void WithSmartPointers(); void WithoutSmartPointers();

int main(int argc, char *argv[]) {

 WithSmartPointers();
 WithoutSmartPointers();
 return 0;

}

void WithSmartPointers() {

 vtkSmartPointer<vtkFloatArray> Distances = vtkSmartPointer<vtkFloatArray>::New();

}

void WithoutSmartPointers() {

 vtkFloatArray* Distances = vtkFloatArray::New();
 Distances->Delete();
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

project(SmartPointers)

find_package(VTK REQUIRED) include(${VTK_USE_FILE})

add_executable(SmartPointers SmartPointers.cpp)

if(VTK_LIBRARIES)

 target_link_libraries(SmartPointers ${VTK_LIBRARIES})

else()

 target_link_libraries(SmartPointers vtkHybrid)
endif()