VTK/Tutorials/SmartPointers: Difference between revisions

From KitwarePublic
< VTK‎ | Tutorials
Jump to navigationJump to search
(misplaced header)
Line 32: Line 32:
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.


However, you may need to wrap a raw pointer because you need to use a constructor that takes arguments. In this situation, use the Take method. This passes the object's self-ownership to the smart pointer:
=== Putting an Existing Object into a Smart Pointer ===
 
You may need to wrap a raw pointer because you need to use a constructor that takes arguments. In this situation, use the Take method. This passes the object's self-ownership to the smart pointer:
<source lang="cpp">
<source lang="cpp">
class MySpecialClass : public vtkObject { /* ... */ };
class MySpecialClass : public vtkObject { /* ... */ };
Line 87: Line 89:


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.
=== Putting an Existing Object into a Smart Pointer ===


=== Using Smart Pointers as Class Member Variables ===
=== Using Smart Pointers as Class Member Variables ===

Revision as of 20:30, 1 February 2010

Idea

The idea behind smart pointers is reference counting. 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. Pretty 'smart', eh?

Usage

Creating an Object with a Smart Pointer

One way to create a VTK object is <source lang="cpp"> 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 <source lang="cpp"> 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

<source lang="cpp">

  1. include <vtkSmartPointer.h>

</source>

Then you can create an object as follows: <source lang="cpp"> 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: <source lang="cpp"> vtkSmartPointer<vtkObject> MyObject = vtkObject::New(); </source>

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

You may need to wrap a raw pointer because you need to use a constructor that takes arguments. In this situation, use the Take method. This passes the object's self-ownership to the smart pointer: <source lang="cpp"> class MySpecialClass : public vtkObject { /* ... */ }; MySpecialClass* MyObjectRaw = new MyObject(arguments); vtkSmartPointer<MySpecialClass> MyObject; MyObject.Take(MyObjectRaw); </source>

Because MyObject now owns the object, there is no need to invoke MyObjectRaw->Delete().

Getting an Object with a Smart Pointer

When not allocating memory for an object, you can still use smart pointers. Take this simple example: <source lang="cpp"> vtkSmartPointer<vtkXMLPolyDataReader> Reader = vtkSmartPointer<vtkXMLPolyDataReader>::New(); vtkPolyData* pd = Reader->GetOutput(); </source> vs <source lang="cpp"> 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.

Returning a Smart Pointer

Correct

You should define a function like this: <source lang="cpp"> vtkSmartPointer<vtkPolyData> MyFunction() {

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

} </source>

And call the function using: <source lang="cpp"> 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.

Incorrect

<source lang="cpp"> vtkPolyData* MyFunction() {

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

}

vtkPolyData* MyPolydata = MyFunction(); </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.

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: <source lang="cpp"> class MyClass {

 vtkSmartPointer<vtkFloatArray> Distances;

}; </source>

You can initialize the smart pointer in your constructor using an initializer: <source lang="cpp"> MyClass::MyClass()

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

{} </source>

Or you can initialize it with an assignment statement: <source lang="cpp"> MyClass::MyClass() {

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

} </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.

Pitfalls

  • If you create an object and then change where it is pointing, the reference count will be incorrect. e.g.

<source lang="cpp"> vtkSmartPointer<vtkPolyData> Polydata = vtkSmartPointer<vtkPolyData>::New(); Polydata = Reader->GetOutput(); </source> In this case, memory is allocated for Polydata, but then we change Polydata to point to the output of Reader rather than the memory we just allocated. Instead, we should have done simply: <source lang="cpp"> vtkPolyData* Polydata = Reader->GetOutput(); </source>

It was not necessary to use a smart pointer because we did not actually create any new objects.

Example

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

SmartPointers.cpp

<source lang="cpp">

  1. include <vtkFloatArray.h>
  2. 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();

}

</source>

CMakeLists.txt

<source lang="text"> cmake_minimum_required(VERSION 2.6)

PROJECT(SmartPointers)

FIND_PACKAGE(VTK REQUIRED) INCLUDE(${VTK_USE_FILE})

ADD_EXECUTABLE(SmartPointers SmartPointers.cpp) TARGET_LINK_LIBRARIES(SmartPointers vtkHybrid)

</source>