Documentos de Académico
Documentos de Profesional
Documentos de Cultura
C++ supports three basic types of memory allocation, of which youve already seen two.
Static memory allocation happens for static and global variables. Memory for these
types of variables is allocated once when your program is run and persists throughout the
life of your program.
Automatic memory allocation happens for function parameters and local variables.
Memory for these types of variables is allocated when the relevant block is entered, and
freed when the block is exited, as many times as necessary.
Most of the time, this is just fine. However, you will come across situations where one or both of
these constraints cause problems, usually when dealing with external (user or file) input.
For example, we may want to use a string to hold someones name, but we do not know how
long their name is until they enter it. Or we may want to read in a number of records from disk,
but we dont know in advance how many records there are. Or we may be creating a game, with
a variable number of monsters (that changes over time as some monsters die and new ones are
spawned) trying to kill the player.
If we have to declare the size of everything at compile time, the best we can do is try to make a
guess the maximum size of variables well need and hope thats enough:
First, it leads to wasted memory if the variables arent actually used. For example, if we allocate
25 chars for every name, but names on average are only 12 chars long, were using over twice
what we really need. Or consider the rendering array above: if a rendering only uses 10,000
polygons, we have 20,000 Polygons worth of memory not being used!
Second, how do we tell which bits of memory are actually used? For strings, its easy: a string
that starts with a \0 is clearly not being used. But what about monster[24]? Is it alive or dead
right now? That necessitates having some way to tell active from inactive items, which adds
complexity and can use up additional memory.
Third, most normal variables (including fixed arrays) are allocated in a portion of memory called
the stack. The amount of stack memory for a program is generally quite small -- Visual Studio
defaults the stack size to 1MB. If you exceed this number, stack overflow will result, and the
operating system will probably close down the program.
On Visual Studio, you can see this happen when running this program:
1 int main()
2 {
3 int array[1000000]; // allocate 1 million integers (probably 4MB of memory)
4 }
Being limited to just 1MB of memory would be problematic for many programs, especially those
that deal with graphics.
Fourth, and most importantly, it can lead to artificial limitations and/or array overflows. What
happens when the user tries to read in 600 records from disk, but weve only allocated memory
for a maximum of 500 records? Either we have to give the user an error, only read the 500
records, or (in the worst case where we dont handle this case at all) overflow the record array
and watch something bad happen.
Fortunately, these problems are easily addressed via dynamic memory allocation. Dynamic
memory allocation is a way for running programs to request memory from the operating system
when needed. This memory does not come from the programs limited stack memory -- instead,
it is allocated from a much larger pool of memory managed by the operating system called the
heap. On modern machines, the heap can be gigabytes in size.
To allocate a single variable dynamically, we use the scalar (non-array) form of the new
operator:
Most often, well assign the return value to our own pointer variable so we can access the
allocated memory later.
int *ptr = new int; // dynamically allocate an integer and assign the address to ptr so we can
1
access it later
If it wasnt before, it should now be clear at least one case in which pointers are useful. Without a
pointer to hold the address of the memory that was just allocated, wed have no way to access the
memory that was just allocated for us!
Your computer has memory (probably lots of it) that is available for applications to use. When
you run an application, your operating system loads the application into some of that memory.
This memory used by your application is divided into different areas, each of which serves a
different purpose. One area contains your code. Another area is used for normal operations
(keeping track of which functions were called, creating and destroying global and local variables,
etc). Well talk more about those later. However, much of the memory available just sits there,
waiting to be handed out to programs that request it.
When you dynamically allocate memory, youre asking the operating system to reserve some of
that memory for your programs use. If it can fulfill this request, it will return the address of that
memory to your application. From that point forward, your application can use this memory as it
wishes. When your application is done with the memory, it can return the memory back to the
operating system to be given to another program.
Unlike static or automatic memory, the program itself is responsible for requesting and disposing
of dynamically allocated memory.
When you dynamically allocate a variable, you can also initialize it via direct initialization or
uniform initialization (in C++11):
The delete operator does not actually delete anything. It simply returns the memory being
pointed to back to the operating system. The operating system is then free to reassign that
memory to another application (or to this application again later).
Although it looks like were deleting a variable, this is not the case! The pointer variable still has
the same scope as before, and can be assigned a new value just like any other variable.
Note that deleting a pointer that is not pointing to dynamically allocated memory may cause bad
things to happen.
Dangling pointers
C++ does not make any guarantees about what will happen to the contents of deallocated
memory, or to the value of the pointer being deleted. In most cases, the memory returned to the
operating system will contain the same values it had before it was returned, and the pointer will
be left pointing to the now deallocated memory.
1 #include <iostream>
2
3 int main()
4 {
5 int *ptr = new int; // dynamically allocate an integer
6 *ptr = 7; // put a value in that memory location
7
8 delete ptr; // return the memory to the operating system. ptr is now a dangling pointer.
9
10 std::cout << *ptr; // Dereferencing a dangling pointer will cause undefined behavior
11 delete ptr; // trying to deallocate the memory again will also lead to undefined behavior.
12
13 return 0;
14 }
In the above program, the value of 7 that was previously assigned to the allocated memory will
probably still be there, but its possible that the value at that memory address could have
changed. Its also possible the memory could be allocated to another application (or for the
operating systems own usage), and trying to access that memory will cause the operating system
to shut the program down.
Deallocating memory may create multiple dangling pointers. Consider the following example:
#include <iostream>
1
2
int main()
3
{
4
int *ptr = new int; // dynamically allocate an integer
5
int *otherPtr = ptr; // otherPtr is now pointed at that same memory location
6
7
delete ptr; // return the memory to the operating system. ptr and otherPtr are now dangling
8
pointers.
9
ptr = 0; // ptr is now a nullptr
10
11
// however, otherPtr is still a dangling pointer!
12
13
return 0;
14
}
Rule: To avoid dangling pointers, after deleting memory, set all pointers pointing to the deleted
memory to 0 (or nullptr in C++11).
When requesting memory from the operating system, in rare circumstances, the operating system
may not have any memory to grant the request with.
By default, if new fails, a bad_alloc exception is thrown. If this exception isnt properly handled
(and it wont be, since we havent covered exceptions or exception handling yet), the program
will simply terminate (crash) with an unhandled exception error.
In many cases, having new throw an exception (or having your program crash) is undesirable, so
theres an alternate form of new that can be used instead to tell new to return a null pointer if
memory cant be allocated. This is done by adding the constant std::nothrow between the new
keyword and the allocation type:
int *value = new (std::nothrow) int; // value will be set to a null pointer if the integer allocation
1
fails
In the above example, if new fails to allocate memory, it will return a null pointer instead of the
address of the allocated memory.
Note that if you then attempt to dereference this memory, your program will crash. Consequently,
the best practice is to check all memory requests to ensure they actually succeeded before using
the allocated memory.
1 int *value = new (std::nothrow) int; // ask for an integer's worth of memory
2 if (!value) // handle case where new returned null
3 {
4 std::cout << "Could not allocate memory";
5 exit(1);
6 }
Because asking new for memory only fails rarely (and almost never in a dev environment), its
common to forget to do this check!
Null pointers (pointers set to address 0 or nullptr) are particularly useful when dealing with
dynamic memory allocation. In the context of dynamic memory allocation, a null pointer
basically says no memory has been allocated to this pointer. This allows us to do things like
conditionally allocate memory:
Deleting a null pointer has no effect. Thus, there is no need for the following:
1 if (ptr)
2 delete ptr;
1 delete ptr;
If ptr is non-null, the dynamically allocated variable will be deleted. If it is null, nothing will
happen.
Memory leaks
Dynamically allocated memory effectively has no scope. That is, it stays allocated until it is
explicitly deallocated or until the program ends (and the operating system cleans it up, assuming
your operating system does that). However, the pointers used to hold dynamically allocated
memory addresses follow the scoping rules of normal variables. This mismatch can create
interesting problems.
Consider the following function:
1 void doSomething()
2 {
3 int *ptr = new int;
4 }
This function allocates an integer dynamically, but never frees it using delete. Because pointers
follow all of the same rules as normal variables, when the function ends, ptr will go out of scope.
Because ptr is the only variable holding the address of the dynamically allocated integer, when
ptr is destroyed there are no more references to the dynamically allocated memory. This is called
a memory leak. As a result, the dynamically allocated integer can not be deleted, and thus can
not be reallocated or reused while the program is running. Memory leaks eat up free memory
while the program is running, making less memory available not only to this program, but to
other programs as well. Programs with severe memory leak problems can eat all the available
memory, causing the entire machine to run slowly or even crash.
Memory leaks can also result if the pointer holding the address of the dynamically allocated
memory is reassigned to another value:
1 int value = 5;
2 int *ptr = new int; // allocate memory
3 ptr = &value; // old address lost, memory leak results
The address returned from the second allocation overwrites the address of the first allocation.
Consequently, the first allocation becomes a memory leak!
Conclusion
Operators new and delete allow us to dynamically allocate single variables for our programs.
Dynamically allocated memory has no scope and will stay allocated until you deallocate it or the
program terminates.
In the next lesson, well take a look at using new and delete to allocate and delete arrays.
Class members that are objects - Pointers or
not? C++
Ask Question
If I create a class MyClass and it has some private member say MyOtherClass, is it
better to make MyOtherClass a pointer or not? What does it mean also to have it as
not a pointer in terms of where it is stored in memory? Will the object be created
when the class is created?
I noticed that the examples in QT usually declare class members as pointers when
they are classes.
up vote 20
down vote Regards
favorite
13 Mark
shareedit
rursw1 Mark
2,31122661 66631326
1 Code is better than English as a description. Loki Astari Oct 6 '10 at 11:55
add a comment
11 Answers
active oldest votes
up vote 23
down vote If I create a class MyClass and it has some private member say MyOtherClass, is it
accepted better to make MyOtherClass a pointer or not?
you should generally declare it as a value in your class. it will be local, there will be
less chance for errors, fewer allocations -- ultimately fewer things that could go
wrong, and the compiler can always know it is there at a specified offset so... it
helps optimization and binary reduction at a few levels. there will be a few cases
where you know you'll have to deal with pointer (i.e. polymorphic, shared, requires
reallocation), it is typically best to use a pointer only when necessary - especially
when it is private/encapsulated.
What does it mean also to have it as not a pointer in terms of where it is stored in
memory?
its address will be close to (or equal to) this -- gcc (for example) has some
advanced options to dump class data (sizes, vtables, offsets)
shareedit
justin
91.3k10146193
The big downside to this in larger projects is it forces a #include of the header
where MyOtherClass is declared. This can quickly lead to very slow
8
compilation times. If you use a (smart) pointer, you can get away with a
forward declaration. Ben Oct 7 '10 at 19:26
@Ben +1 yes - i failed to mention intermodule dependencies and abstraction of
them in my post. this is a very important reason to favor dynamically allocated
members in some cases. justin Oct 8 '10 at 5:08
add a comment
up vote 19
down vote Where is your member stored in memory?
Take a look at this example:
void bar() {
A a_stack; // a_stack is on stack
// a_stack.foo is on stack too
A* a_heap = new A(); // a_heap is on stack (it's a pointer)
// *a_heap (the pointee) is on heap
// a_heap->foo is on heap
B b_stack; // b_stack is on stack
// b_stack.foo is on stack
// *b_stack.foo is on heap
B* b_heap = new B(); // b_heap is on stack
// *b_heap is on heap
// b_heap->foo is on heap
// *(b_heap->foo is on heap
delete a_heap;
delete b_heap;
// B::~B() will delete b_heap->foo!
}
We define two classes A and B. A stores a public member foo of type Foo. B has a
member foo of type pointer to Foo.
If you create a variable a_stack of type A on the stack, then the object
(obviously) and its members are on the stack too.
If you create a pointer to A like a_heap in the above example, just the pointer
variable is on the stack; everything else (the object and it's members) are on
the heap.
you create B on the stack: then both the object and its member foo are on the
stack, but the object that foo points to (the pointee) is on the heap. In short:
b_stack.foo (the pointer) is on the stack, but *b_stack.foo the (pointee)
is on the heap.
you create a pointer to B named b_heap: b_heap (the pointer) is on the stack,
*b_heap (the pointee) is on the heap, as well as the member b_heap->foo
and *b_heap->foo.
In case of B: If you omit our ctor and dtor then foo (the pointer) will also be
created and initialized with a random number which means that it will point
to a random location on the heap. But note, that the pointer exists! Note
also, that the implicit default constructor won't allocate something for foo
for you, you have to do this explicitly. That's why you usually need an
explicit constructor and a accompanying destructor to allocate and delete
the pointee of your member pointer. Don't forget about copy semantics:
what happens to the pointee if your copy the object (via copy construction or
assignment)?
To point to an object you don't own. Let's say your class needs access to a
huge data structure that is very costly to copy. Then you could just save a
pointer to this data structure. Be aware that in this case creation and
deletion of the data structure is out of the scope of your class. Someone
other has to take care.
Increasing compilation time, since in your header file the pointee does not
have to be defined.
A bit more advanced; When your class has a pointer to another class that
stores all private members, the "Pimpl idiom": http://c2.com/cgi/wiki?
PimplIdiom, take also a look at Sutter, H. (2000): Exceptional C++, p. 99--
119
Advice
Take extra care if your members are pointers and you own them. You have to write
proper constructors, destructors and think about copy constructors and assignment
operators. What happens to the pointee if you copy the object? Usually you will
have to copy construct the pointee as well!
WolfgangA
2,2781732
I don't find thinking in terms of heap/stack very usefull (especially since neither
are really defined by the standard). I think of objects in terms of their lifespan
in relation to the containing block. An object with a scoped life should be an
object. An object that has a dynamic lifespan should be a pointer (stored in a
smart pointer). The only different between a member variable and a function
variable is their scope. A member variables lifespan is relative to its scope the
object that it resides in. While a function variables is relative to its scope the
function (or block). Loki Astari Oct 6 '10 at 12:03
That's definitly true, but the question was where the objects are stored in
memory, which is useful to sort things out in your head. WolfgangA Oct 6 '10
at 12:08
add a comment
In C++, pointers are objects in their own right. They're not really tied to whatever they
point to, and there's no special interaction between a pointer and its pointee (is that a
word?)
If you create a pointer, you create a pointer and nothing else. You don't create the
object that it might or might not point to. And when a pointer goes out of scope, the
pointed-to object is unaffected. A pointer doesn't in any way affect the lifetime of
whatever it points to.
up vote So in general, you should not use pointers by default. If your class contains another
15 down object, that other object shouldn't be a pointer.
vote
However, if your class knows about another object, then a pointer might be a good
way to represent it (since multiple instances of your class can then point to the same
instance, without taking ownership of it, and without controlling its lifetime)
shareedit
jalf
185k36262497
On the other hand, PIMPL is all about cutting down the dependencies by
2 introducing a layer of indirection in the visibility. Matthieu M. Oct 6 '10 at
12:19
add a comment
up vote 5
down The common wisdom in C++ is to avoid the use of (bare) pointers as much as
vote possible. Especially bare pointers that point to dynamically allocated memory.
The reason is because pointers make it more difficult to write robust classes,
especially when you also have to consider the possibility of exceptions being thrown.
I follow the following rule: if the member object lives and dies with the encapsulating
object, do not use pointers. You will need a pointer if the member object has to outlive
the encapsulating object for some reason. Depends on the task at hand.
Usually you use a pointer if the member object is given to you and not created by you.
up vote 3 Then you usually don't have to destroy it either.
down
vote answered Oct 6 '10 at 10:29
shareedit
chvor
62464
add a comment
up vote 3
down Some advantages of pointer member:
vote
The child (MyOtherClass) object can have different lifetime than its parent
(MyClass).
The object can possibly be shared between several MyClass (or other) objects.
When compiling the header file for MyClass, the compiler doesn't necessarily
have to know the definition of MyOtherClass. You don't have to include its
header, thus decreasing compile times.
Makes MyClass size smaller. This can be important for performance if your
code does a lot of copying of MyClass objects. You can just copy the
MyOtherClass pointer and implement some kind of reference counting system.
You don't have to explicitely write code to create and destroy the object. It's
easier and and less error-prone.
More intuitive
shareedit
Timo
3,79731622
add a comment
If MyOtherClass is a pointer:
up vote 3
down vote The creation and destruction of MyOtherClass is your responsibility
MyOtherClass may be NULL, which could have meaning in your context and
could save memory
shareedit
Drew Dormann
35.4k965125
add a comment
up vote 1
down An advantage of the parent class maintaining the relation to a member object as a
vote (std::auto_ptr) pointer to the member object is that you can forward declare the object
rather than having to include the object's header file.
This decouples the classes at build time allowing to modify the member object's
header class without causing all the clients of your parent class to be recompiled as
well even though they probably do not access the member object's functions.
When you use an auto_ptr, you only need to take care of construction, which you
could typically do in the initializer list. Destruction along with the parent object is
guaranteed by the auto_ptr.
shareedit
andreas buykx
6,41664072
add a comment
up vote 1
down vote It depends... :-)
If you use pointers to say a class A, you have to create the object of type A e.g. in
the constructor of your class
Moreover, don't forget to destroy the object in the destructor or you have a memory
leak:
delete m_pA;
m_pA = NULL;
Instead, having an object of type A aggregated in your class is easier, you can't forget
to destroy it, because this is done automatically at the end of lifetime of your object.
If your object is allocated on the stack and type A uses a lot of memory this
won't be allocated from the stack but from the heap.
You can construct your A object later (e.g. in a method Create) or destroy it
earlier (in method Close)
up vote 1 You might want to keep MyOtherClass as a pointer member because it gives you the
down vote flexibility to point it to any other class that is derived from it. Basically helps you
implement dynamice polymorphism.
shareedit
Alok Save
145k26295439
add a comment
up vote 0
down The simple thing to do is to declare your members as objects. This way, you do not
vote have to care about copy construction, destruction and assignment. This is all taken
care of automatically.
However, there are still some cases when you want pointers. After all, managed
languages (like C# or Java) actually hold member objects by pointers.
The most obvious case is when the object to be kept is polymorphic. In Qt, as you
pointed out, most objects belong to a huge hierarchy of polymorphic classes, and
holding them by pointers is mandatory since you don't know at advance what size will
the member object have.
Please beware of some common pitfalls in this case, especially when you deal with
generic classes. Exception safety is a big concern:
struct Foo
{
Foo()
{
bar_ = new Bar();
baz_ = new Baz(); // If this line throw, bar_ is never
reclaimed
// See copy constructor for a workaround
}
Foo(Foo const& x)
{
bar_ = x.bar_.clone();
try { baz_ = x.baz_.clone(); }
catch (...) { delete bar_; throw; }
}
private:
Bar* bar_;
Baz* baz_;
};
As you see, it is quite cumbersome to have exception safe constructors in the presence
of pointers. You should look at RAII and smart pointers (there are plenty of resources
here and somewhere else on the web).