More Effective C ++ Terms 26 (below)

zhaozj2021-02-08  265

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 :: counted ()

{INIT ();

Template

Counted :: Counted (const counted )

{INIT ();

Template

Void counted :: init ()

{

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 :: objectcount; // See Explanation below

Using Counted :: toomanyObjects; // See Explanation below

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 , but we must give a virtual destructuring function to Counted classes. (Otherwise, if someone removes a Printer object through the Counted * pointer, we have the risk of incorrect object behavior - see Effective C Terms 14.) Terms 24 I have already said it is very clear, there is a virtual function in Counted Almost certainly affect the size and layout of the objects that inherited from Counted. We don't want to introduce these additional burdens, so we use private inheritance to avoid these burdens.

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 :: ObjectCount; // Let this function for Printer

// 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 :: Objectcount; // Let ObjectCount

// 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 , it can forget things about object counts. When writing the Printer class, you don't have to consider the object count at all, as if you have anyone counts it. The constructor of the Printer can be like this:

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 , because counted is the base class of Printer, we know that the constructor of Counted is always called in front of the Printer. If you create too many objects, the constructor of counted throws an exception, and even invoke the constructor of the Printer. In the end, it is also important to pay attention to define static members in Counted. For NumObjects, this is easy - we only need to define it in the Counted implementation file:

Template // Define NumObjects

INT Counted :: NumObjects; // Initialize it to 0

There are some techniques for maxObjects. Should we initialize it why? If you want to allow 10 Printer objects, we should initialize the Counted :: maxObjects is 10. On the other hand, if we want to establish 16 file descriptor objects, we should initialize Counted :: maxObjects for 16. What should I do?

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 :: maxObjects = 10;

The same author of FileDescriptor also has to join this statement:

Const size_t counted :: maxObjects = 16;

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.

转载请注明原文地址:https://www.9cbs.com/read-981.html

New Post(0)