Whenever an object is created, space is allocated in one of four possible places. Each of these places is called a storage class.
While scope refers to a region of code where an identifier is accessible, storage class refers to a location in memory.
- The static area: Global variables, static locals, and static data members are all stored in the static storage area. The lifetime of a static object begins when its object module loads and ends when the program terminates. It is used often for pointers, simple types, and string constants, less often for complex objects.
- The program stack (automatic storage—auto5): Local variables, function parameters, and temporary objects are all stored on the stack. For local (block-scope) variables, the lifetime is determined by the brackets around the code that is executed. Objects on the stack include function parameters, return values, local or temporary variables. Stack storage is allocated automatically when an object definition is executed. Objects in this storage class are local to a function or a block of statements.
- The heap or free storage (dynamic storage): Objects created via new are examples. The lifetime of a heap object is determined entirely by the use of new and delete. In general, the allocation and freeing of heap objects should be in carefully encapsulated classes.
- Another storage class, left over from C, is called register. It is a specialized form of automatic storage. This category of storage can be requested by using the keyword, register in the variable declaration.Most C++ compilers ignore this keyword and put such variables on the stack. Using this storage class for an object means that you cannot take its address.
Globals, statics, and QObject
Global objects need to be “global” for two reasons.
- They need a lifetime that is the same as the application.
- They need to be available from many different places in the application.
In C++, we avoid the use of global scope at all costs, in favor of other approaches.
We still use global scope identifiers for the following:
- Class names
- Namespace names
- The global pointer qApp, which points to the running QApplication object.
By turning a global object into a static class member, or a namespace member, we can avoid increasing the size of the global namespace while keeping the object accessible from other source code modules.
Note:
statics and QObject
When creating QObject or other interesting classes, it is important that their destructors do not get called after the QApplication has been destroyed (after main()is finished).
static QObjects (and other complex objects) cleaned up after a
QApplication has been destroyed could have difficulties in clean-up code. This is because QApplication, when it is destroyed, takes a lot of other objects with it.
In general, we want to have control over the order of destruction of all complex objects. One way to ensure this is to make each QObject allocated on the stack, or on the heap, and then added as a child/grandchild of another QObject already on the stack.
The QApplication (or its derived instance) is the “rootiest” stack object of them all, so we try to make it the “last QObject standing.”
Globals and const
The scope of const variables is slightly different from the scope of regular globals. A global object that has been declared const has file scope, by default. It may be exported to other files by declaring it extern at the point where it is initialized.
For example, in one file, we could have this:
const int NN = 10; //must be initialized
//declaration and definition - allocates storage
extern const int QQ = 7;
int main() {
NN = 12; // Error!
int array[NN]; // OK
QQ++; // Error!
return 0;
}
In another file, we might have
const int NN = 33; // a different constant
// declare global constant - storage allocated elsewhere
extern const int QQ; // external declaration
...
The second file has a const int NN that is separate and distinct from the const with the same name in the first file. The second file can share the use of the const int QQ because of the extern modifier.
Exercise:
In Example below, identify the scope/storage class of each of object’s creation/definition. If there is a name clash, indicate there is an error with the definition.
/*
Let us assume that we have a namespace “qstd”, there we have declared a variable (you can assume of your type based on the code below) of type QString.
*/
#include <qstd.h>
#include <iostream>
using namespace qstd;
int i; // Scope: ______ Storage class: ______
static int j; // Scope: ______ Storage class: ______
extern int k; // Scope: ______ Storage class: ______
const int l=10; // Scope: ______ Storage class: ______
extern const int m=20; // Scope: ______ Storage class: ______
class Point { // Scope: ______ Storage class: ______
public:
QString name; // Scope: ______ Storage class: ______
QString toString() const;
private:
static int count;
int x, y; // Scope: ______ Storage class: ______
};
int Point::count = 0;
QString Point::toString() const {
return QString("(%1,%2)").arg(x).arg(y); // Scope: ______ Storage class: ______
}
int main(int argc, char** argv) { // Scope: ______ Storage class: _
int j; // Scope: ______ Storage class: ______
register int d; // Scope: ______ Storage class: ______
int* ip = 0; // Scope: ______ Storage class: ______
ip = new int(4); // Scope: ______ Storage class: ______
Point p; // Scope: ______ Storage class: ______
Point* p2 = new Point();// Scope: ______ Storage class: ______
}
No comments:
Post a Comment