Terms 26: Limit the number of objects that can be generated in a class (below)
Allow objects to go free
We know how to design only the class of an instance, we know that the number of objects that track the specific class is complex, because in three different environments, the object constructor can be called, we know that the object count is eliminated. The method is to declare the constructor as private. There is also the last point worth noting: Use the Theprinter function to encapsulate access to a single object so that the number of the Printer object is limited to one, which will also make us only use a Printer object when running the program every time. Leading us not to write code like this:
Establish a Printer object P1;
Use P1;
Release P1;
Establish a Printer object P2;
Use P2;
Release P2;
...
This design does not instant multiple Printer objects in the same time, but uses different PRINTER objects in different parts of the program. This is not allowed to write some unreasonable. After all, we have no violations that can only exist a Printer constraint. Is there any way to legalize it?
Of course. We must combine the code that the previously used object count with the pseudo-creation function code you just saw:
Class printer {
PUBLIC:
Class toomanyObjects {};
// pseudogenic function
Static Printer * makeprinter ();
~ Printer ();
Void SubmitJob (Const PrintJob & Job);
Void reset ();
Void Performselftest ();
...
Private:
Static size_t numoBjects;
PRINTER ();
Printer (const printer & rhs); // We don't define this function
}; // Because it is not allowed
/ / Copy
// (See Effective C Terms 27)
// Obligatory definition of class static
SIZE_T Printer :: NumObjects = 0;
Printer :: printer ()
{
IF (NumObjects> = 1) {
Throw toomanyObjects ();
}
Continue to run the normal constructor;
NumObjects;
}
Printer * printer :: makeprinter ()
{Return new printer;}
When the object needs to be too much, it will throw an exception. If you think this way is a unreasonably harsh, you can let the pseudo-constructor return an empty pointer. Of course, the client should detect before use.
In addition to the client must call the pseudo-constructor, instead of the real constructor, they use the Printer class as using other classes:
Printer P1; // Error! The default constructor is
// private
Printer * p2 =
Printer :: makeprinter (); // correct, indirect call
/ / Default constructor
Printer P3 = * P2; // Error! Copy constructor is
// private
P2-> Performselftest (); // All other functions can
P2-> reset (); // Normal call
...
Delete P2; // Avoid memory leaks, if // p2 is an auto_ptr,
// Don't need this step.
This technology is easy to extend to restrictions on any number. We only need to change the Hard-Wired constant value 1 to the number determined according to a certain class, then eliminate the constraint of the copy object. For example, the code implemented below this modified Printer class, allowing up to 10 Printer objects to exist:
Class printer {
PUBLIC:
Class toomanyObjects {};
// pseudogenic function
Static Printer * makeprinter ();
Static Printer * Makeprinter (Const Printer & RHS);
...
Private:
Static size_t numoBjects;
Static const size_t maxObjects = 10; // See explanation below
PRINTER ();
PRINTER (Const Printer & RHS);
}
// Obligatory definitions of class statics
SIZE_T Printer :: NumObjects = 0;
Const size_tprinter :: maxObjects;
Printer :: printer ()
{
IF (NumObjects> = maxObjects) {
Throw toomanyObjects ();
}
...
}
Printer :: Printer (Const Printer & RHS)
{
IF (NumObjects> = maxObjects) {
Throw toomanyObjects ();
}
...
}
Printer * printer :: makeprinter ()
{Return new printer;}
Printer * Printer :: Makeprinter (Const Printer & RHS)
{RETURN New Printer (RHS);
If your compiler can't compile the statement :: maxObjects in the above class, this is not surprising. In particular, it should be prepared, the compiler cannot compile 10 statements to this variable for the initial value. Give static const members (such as int, char, enum, etc.) Determine the function of the initial value to get to C recently, so some compilers do not allow this to write this. If you don't update your compiler in time, you can declare the maxObjects as enumeration elements in a private in a private,
Class printer {
Private:
Enum {maxObjects = 10}; // In the class,
... // maxObjects is a constant 10
}
Or initialize Static constants like Non-Const Static members:
Class printer {
Private:
Static const size_t maxObjects; // does not assign the initial value
...
}
// Put in a file implemented by a code
Const size_tprinter :: maxObjects = 10;
The following methods have the same effect as the original method, but it is displayed to determine that the initial value can make other programmers easier to understand. When your compiler supports the functionality of the initial value to the Const Static member, you should use this feature as much as possible.
A base class with an object count function
The initialized static member is not said, the above method uses like a spell, but on the other hand it is a bit cumbersome. If we have a large number of classes such as Printer to limit the number of instances, we must write the same code over and over again, and each class is written once. This will make the brain become numb. There should be a way to automatically process these things. Is there any way to install an instance idea in a class?
We can easily write a base class with an instance count function, then let the classes like Printer from the base class, and we can do better. We use a method package that all count functions, not only package the function of the instance counter, but also the instance counter itself. (When we test the reference count in Terms 29, we will see the needs of the same technique. For the test details of this design, see my article counting objects.
The PRINTER class counter is a static variable NumObjects, and we should put the variable in an instance count class. However, it is also necessary to ensure that the classes of each instance count have a counter isolated counter. Using the count class template can automatically generate an appropriate number of counters because we can make the counter a static member of the class generated from the template:
Template
Class counted {
PUBLIC:
Class toOManyObjects {}; // Used to throw an exception
Static int Objectcount () {Return NumObjects;
protected:
Counted ();
COUNTED (Const Counted & RHS);
~ Counted () {--NumObjects;
Private:
Static int numObjects;
Static const size_t maxObjects;
Void init (); / / avoid constructor
}; // code repeat
Template
Counted
{INIT ();
Template
Counted
{INIT ();
Template
Void counted
{
IF (NumObjects> = maxObjects) throw toomanyObjects ();
NumObjects;
}
The class generated from this template can only be used as the base class, so the constructor and the destructor are declared as protected. Note that the private member function init is used to avoid repetition of the statement of the two Counted constructor.
Now we can modify the Printer class, so use the Counted Template:
Class Printer: Private Counted
PUBLIC:
// pseudogenic function
Static Printer * makeprinter ();
Static Printer * Makeprinter (Const Printer & RHS);
~ Printer ();
Void SubmitJob (Const PrintJob & Job);
Void reset ();
void performselftest (); ...
Using Counted
Using Counted
Private:
PRINTER ();
PRINTER (Const Printer & RHS);
}
Printer Template uses a PRINTER object to track how much Printer objects, frankly, in addition to Printer's writers, no one cares about this fact. Its implementation details are best private, which is why it uses Private inheritance (see Effective C Terms 42). Another way is to use public inheritance between printer and counted
Most of the work made by Printer is hidden for Printer's client, but these clients may want to know how much the printer object is present. The COUNTED template provides the ObjectCount function to provide this information, but because we use private inheritance, this function becomes private in the Printer class. In order to restore the PUBLIC access to the function, we use using statement:
Class Printer: Private Counted
PUBLIC:
...
Using counted
// is public
...
}
This is the symptom rules, but if your compiler does not support namespace, the compiler does not allow this. If this is, you should use the vintage access statement grammar:
Class Printer: Private Counted
PUBLIC:
...
Counted
// In Printer, PUBLIC
...
}
This more traditional syntax has the same meaning as the uning statement. But we don't agree with this. The ToOManyObjects class should also apply the same way, because Printer's clients are to capture this exception type, they must have the ability to access toomanyObjects. .
When Printer inherits Counted
Printer :: printer ()
{
Perform normal constructor operation
}
Interesting here is not what you see, but what you can't see. The number of non-detected objects seems to be limited to exceeded, and there is no increase in the number of objects after performing the finite function. All of this is now handled by the constructor of counted
Template
INT Counted
There are some techniques for maxObjects. Should we initialize it why? If you want to allow 10 Printer objects, we should initialize the Counted
A simple way is nothing. We don't initialize MaxObject. Instead, such clients provide appropriate initialization. The author of Printer must join this statement into an implementation file:
Const size_t counted
The same author of FileDescriptor also has to join this statement:
Const size_t counted
What happens if these authors have forgotten to initialize maxObjects? Very simple: errors occur when connecting, because maxObjects are not defined. If we provide a full document to the Counted client, they will go back and add this necessary initialization.
Translator Note:
TRANSLATION UNIT - A Source File Presented to a Complier with an Object File Produced as a result.
Linkage - Refers to WHether a name is visible inly inside or also outside its translation unit.