Counting Objects in C ++

xiaoxiao2021-03-06  56

Calculate the number of objects in C Objects Counting IN C (C User's Journal, 1998/04)

Author: Scott Meyers Translator: Chen Wei

In C , the Objects generated by a Class keeps the correct count, which is available unless you face some crazy parts.

Houjie Note: This article is the article in Beijing "Programmer" magazine 2001/08. The translation is smooth and the technology is full. Mr. Chen Mr. Chen Mr. Chen, a person in charge of "Programmer" magazine, is reproduced here, thank you for Taiwan readers. Mr. Chen, Mr. Chen and Mr. Jiang Tao did not reprint this article.

Translation: This article publishes the date than C Standard early, but the content in the text complies with C Standard. The translation retains the original timing and has not been modified. The following is a few species of the species that use the translation to readers: Client: Nip. TYPE: Type. In order to avoid confusion with other approximation terms, this is "type" rather than "type". Instantiated: That is already. "For a Template, you can realize a entity". Instance: entity. "Case" is an absolute error. Parameter: number. Or the number of types, shapes. Argument: quotes. Or is a substantial number, As for the terms such as Class, Object, Data Member, Member Function, Constructor, Destructor, Template, etc., are reserved.

Sometimes, it is easy, but they still hide some subtle. For example, suppose you have a Class named widget, you want some way to find out how many Widget Objects exist during the execution of the program. One of the methods (not only easy to do, and the answer is correct) is to prepare a Static counter for the Widget. Whenever the Widget Constructor is called, the counter is added, and the counter will be reduced by the counter. In addition, you also need a Static member's functional howmany (), which is used to return how many Widget Objects present. If widget is not done, single just track its objects, then it looks approximately like this:

Class widget {public: ??? widget () { count;} ??? widget (const widget &) { count;} ??? ~ widget () {--count;} ??? static size_t howmany () ??? {return count;} private: ??? static size_t count;}; // count definition. This should be placed in a actual file. SIZE_T WIDGET :: count = 0;

This can work effectively. Don't forget to make a Copy Constructor because the compiler automatically generates the Copy Constructor generated by Widgets will not know to add COUNT.

If you only need to count for widget, you have completed your task. But sometimes you have to do the same count for many? Classes. The repeated work will be done and hate, and the dull and disgust will result in the incorrect occurrence. In order to prevent this situation, it is best to package the "Object-Counting) program code package so that it can be repeatedly used by any Class. The ideal packaging should meet the following conditions: · ???????? Easy to use- Make those Class designers who need this service only the minimum amount of work. The most ideal situation is that they don't have to do anything, just claim "I want to measure this type of Objects".

· ???????? Efficiency - the user does not need to be subject to any non-necessary space tax or timing tax.

· ???????? Absolute security - It is impossible to suddenly lead to an error measure. (We don't intend to pay attention to those who deliberately destroy, they will deliberately try to confuse measuring. In C , users can always find ways to meet their despicable and distressed behavior)

Pause, think about it, how do you make a reusable "object count" suite and meet all the above goals. This may be difficult than you expected. If it is really as easy as you think, you will not see this article on this publication.

New, Delete, and Exceptions

Although you are wholeheartedly solve the "object count" related issues, please allow me to temporarily switch the focus to a seemingly irrelevant topic. This topic is "When constructors throws out the relationship between NEW and DELETE." When you ask C dynamically configure an Object, you will use the new operation in this way:

Class abcd {...}; // abcd = "a big complex datatype"

Abcd * p = new abcd; // This is a New operational

The meaning of this New operation is determined in the language level, and its behavior cannot be changed anyway. It makes two things. First, it calls a memory configuration function called Operator New. This functional responsibility is to find a memory that is sufficient to put an Abcd Object. If the configuration is successful, the New operation will then evoke an ABCD Constructor to create an ABCD Object in the space identified in Operator New.

However, suppose how the Operator New will leave a std :: bad_alloc abnormally, what will happen? This abnormality represents that the task of dynamically configuring the memory failed. In the above New arithmetic formula, there are two functions that may have such an exception. The first is Operator New, which attempts to find enough memory to place an Abcd Object. The second is the ABCD Constructor that is executed, which is attempt to convert the fresh memory into an effective ABCD Object.

If the exception comes from Operator New, it means that no memory is configured. However, if the Operator New is successful and ABCD Constructor is an exception, it is important to release the memory configured by the Operator New. If so, the program will have a loss of memory (Memory Leak). The passenger (that is, the program code that requires an abcd object) cannot know which one of the versions will make an exception.

For many years, this is a vulnerability in the C standard draft, March 1995, the C Standard Committee adopted a proposal: The system (runtime system) must automatically release the memory configured by the Operator New. This release action is executed by Operator Delete, which is similar to Operator New. (For details, "Placement New and Placement Delete" in the last square column.) This "New Archforming and Operator Delete relationship", will affect the "object count mechanism" automation in our attempt .

Calculate the number of objects (Counting Objects)

There is a solution to the object count problem involves developing a "object count special" Class. This Class looks like (even completely like) Widget Class:

// There are some discussions later, tell you why this design is not very correct

?

Class counter {?

PUBLIC: ?????????

???? counter () { count;}

??? counter (const counter &) { count;}

???? ~ counter () {--count;}

??? static size_t howmany ()

??????? {return count;}

?

Private:

??? static size_t count;

}

?

// The following line should still be placed in a actual file.

SIZE_T Counter :: count = 0;

The idea here is that any classes need to record the number of objects in the present, simply use Counter to work as a bookkeeping. There are two obvious ways to complete this task, one is to define a Counter Object, making it a Class Data MEMBER, like this:

// Inside the CLASS that needs to be counted in the Claimer Object.

Class widget {

PUBLIC:

??? ..... // Widget All public members,

?????????? // is placed here.

??? static size_t howmany ()

??? {return counter :: howmany ();

Private:

??? ..... // Widget Whole Private member,

?????????? // is placed here.

??? Counter C;

}

Another method is to make the counter as Base Class, like this:

/ / Let the Class you need to inherit from Counter

Class Widget: public counter {

??? ..... // Widget All public members,

?????????? // is placed here.

Private:

??? ..... // Widget Whole Private member,

??????? ??? // is placed here.

}

Two kinds of practices have good and disadvantageous. Before verifying, we must pay attention to no way, with its current type, can be effectively operated. The problem is in the static object in Counter. Such static objects have only one, but we need to prepare one for each class using COUNTER. For example, if we intend to count the widgets and abcds, we need two static size_t objects instead of one. Let Counter :: Count become NonStatic, and we cannot solve this problem because we need to prepare a counter for each class instead of preparing a counter for each Object. With the most widely known known but names in C , we can get the behavior we want: we can put the counters into a template, then let each Class want to use the COUNTER, "Take yourself for Template The quotes show this template.

Let me say it again. Counter turns into a template:

Template

Class counter {

PUBLIC:

??? counter () { count;}

??? counter (const counter &) { count;}

???? ~ counter () {--count;}

?

??? static size_t howmany ()

??? {return count;}

?

Private:

??? static size_t count;

}

?

Template

Size_t

Counter :: count = 0; // Now this line can put it in the header file.

Then the first actual method of the aforementioned changes is like this:

// Inside the CLASS T in need to embed a Counter Object.

Class widget {

PUBLIC:

??? .....

??? static size_t howmany ()

?? {return counter :: howmany ();

Private:

??? .....

??? Counter C;

}

The second method (inheritance method) changed to this:

/ / Let Class T that need to inherit from Counter

Class Widget: public counter {???

???? .....

}

Note How we are in these two situations to replace Counter as counters. As I said earlier, every Class of using Counter, with itself as a quotes, showing the template.

"In a Class, take yourself for the Template quotes, with a Template, give yourself, this strategy is the first to open by Jim Coplien. He demonstrates this strategy in a variety of languages ​​(not C ), and calls this as a "curiously recurning" [Note 1]. I don't think Jim is deliberately named, but he is much better than this one? Pattern (style, mode) described more than the name he taken. That is really bad, because the name of Pattern is important, and this name you see now, you can't cover "what to do, how to make".

Patterns naming is an art, I am not good at this. However, I may have called this Pattern called the name of "Do IT for ME". At all, each "Class" generated by Counter can provide a service for "COUNTER That is already a Claims", how many Objects are calculated. Therefore, the class counter can calculate the number of objects of the Widget, and the Class Counter can calculate the number of objects of the ABCD. Now, Counter has become a Template, whether it can be operated regardless of the inner embedded design or inheritance design, so we face it next that evaluating its relative intensity and weaknesses. One of our design standards is that the "object count" function should be easy to obtain for users. The above program code clearly tells us that inheritance design is easier than the inner embedded design, because the former only requires that "Counter is a base class", but the latter is required to define a Counter Data Member, and ask the user to make A HOWMANY () to evoge the howmany () [Note 2] of Counter. Although this kind of work is not too much (the customer's howmany () is only a simple inline letter), but only one thing is more than two things. So let us first put the attention in the inheritance design.

Use public inheritance (public inheritance)

The above inheritance design can operate because C guarantees, whenever a Derived Class Object is constructed (or deconsive), where the base class ingredients will be constructed first (or deconstructed). Let Counter becomes a base class to ensure: For the Class of inheritance from the COUNTER, whenever an Object is generated or destroyed, there will be a Counter Constructor or Destructor to be evoked.

However, whenever you involve this theme of Basesses, don't forget Virtual Destructors. Does Counter have such something? Good C object-oriented design specification, yes, it should have a Virtual Destructor. If it doesn't, when we delete a Derived Class Object through a base class point, it will lead to unpredictable (unfained) results, and is usually unpopular:

Class Widget: Public Counter

{...};

Counter * Pw =

??? new widget ;? // get a base class Pointer,

????? ??????????? // point to a derived class Object. ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

......

DELETE PW; // This will result in unknown results - if base clas

?????????? // lack a Virtual Destructor.

Such behavior will violate our demand conditions: Since the above-mentioned program code does not have any unreasonable, our "object count" design should have an absolute manifestation. Therefore, this is a strong reason to have a Virtual Destructor.

Another demand condition is the best efficiency (that is, no necessary speed tax and space tax due to "object count", but we have a bit of trouble here. Because, Virtual Destructor (or any virtual fifth) means that every COUNTER (or its derivative category) Objects must contain a (hidden) virtual indicator, which increases the size of the object - if they originally There is no virtual letter [Note 3]. That is, if the widget itself does not have any virtual fifth, the object that is widget will expand due to inheriting the Counter. We don't want to see this situation. The only way to avoid it is to find a way to prevent the customer from "delete a Derived Class Object through a base class Pointer." Put the Operator Delete in Courge as Private, which seems to be a reasonable approach:

Template

Class counter {

PUBLIC:

??? .....

Private:

??? void operator delete (void *);

??? .....

}

In this way, the Delete operation cannot be compiled:

Class Widget: Public Counter {...};

Counter * pw = new widget ;? ......

DELETE PW; // Error. Because we can't evoke Private Operator Delete

It is unfortunate. However, it is really interesting that the New operation should not be compiled by compilation:

Counter * Pw =

??? new widget ;? // This line should not be compiled,

????????????????? // because Operator delete is private

I will recall the discussion of New, Delete, Excetions, I said that the C executation system is responsible for release the memory configured by the Operator New - if the CONSTRUctor failed after the successor. At the same time, please recall it, Operator delete is a function used to perform memory release actions. Since we declare the COUNTER's Operator Delete as Private, this will make "By NEW, the Objects is generated in HEAP" will never fail.

Yes, this is a violation of intuition, if your compiler does not support it, please don't be surprised. But please note that the behavior I have described is correct. In addition, there is no other obvious method to prevent "Delete Derived Class Objects" through the Counter * Pointer. Since we have rejected the idea of ​​"setting a Virtual Destructor" in counters, it will cause a non-necessary space tax), so I said, let us give up this design, let us put the attention "Use a Counter Data MEMBER "Above.

Use a Data MEMBER

We have seen the disadvantage of "Setting a Counter Data Member" design: the customer must simultaneously define a Counter Data Member and write a inline version of howmany (), used to call the HOWMANY () Verifier . These efforts are more than we want to add to the customer's program, but it is not difficult to control or manage. However, there is another disadvantage in addition to this: adding a COUNTER DATA MEMBER for a Class, often expanding its Objects size. This is almost impossible to have a major revelation. After all, add a Data MEMBER and cause the size of the Objects to increase, will you surprised you? But look at it, think about it, please pay attention to the definition of Counter:

Template

Class counter {

PUBLIC:

??? counter ();

??? COUNTER (Const Counter &);

??? ~ counter ();

?

??? static size_t howmany ();

Private:

??? static size_t count;

}

Please note that it doesn't have nonStatic Data Members, which means that every type is? The Object's object is actually there is anything. Maybe we will think that every type of Object is 0, size is 0? Maybe, but that is not correct. At this point, the performance of C is quite clear. All Objects has at least 1 Byte size, even even if these objects do not have any NonStatic Data Members. According to this definition, SizeOf will get a positive value for each class of COUNTER TEMPLATE. So the Class of each "containing a Counter Object" will have more information from the "not included COUNTER OBJECT".

(Interestingly, this does not mean a "excluding Counter" Class, which has a greater volume than the brothers "containing a Counter". It is because the border arrangement restrictions may result. Effect. For example, if the class widget contains two Bytes information, the system requirements must be arranged in 4-byte, so each Widget Object will contain two Bytes's fillers, while Sizeof (Widget) The result is 4. If it is like a common situation, the compiler satisfies the condition of "any object's size is not possible," then put a char to counters, then SIZEOF (Widget) or back 4, Even if the widget contains a Counter Object. The included Counter Object only replaces one of the two Bytes originally filled. However, this is not a common plot, so we certainly can't design a "object count) When the kit, put it in the plan.)

When I started at the Christmas holiday, I wrote this article (the correct date is Thanksgiving day, this may be able to let you know, how I celebrate this important festival ...), now my emotions are already very All right. What I have to do is only an object count, I don't want to have any strange and extra discussion in Tonglash.

Use private inheritance again to look at the inheritance design code, which leads us to consider a Virtual Destructor for Counter:

Class Widget: Public Counter

{...};

Counter * pw = new widget; ???????????

......

Delete

PW; // leads to undefined (unknown) results-

????? // If counters lacks a Virtual Destructor.

I have tried to stop this series of movements by "preventing the delete operations", but we found that it also stopped the successful compilation of the New operation. In addition, there are other things that we can do. We can prohibit a Widget * Pointer (this is the new return value) is implicitly transformed into a Counter * Pointer. In other words, we can prevent indicator types in the inheritance system from being converted. The only thing we need to do is to change "public inheritance" to "private inheritance":

Class Widget: Private Counter

{...};

Counter * Pw =

??? new widget ;? // error! There is no implicit conversion function (Implicit Conversion)

?????????????????? // convert a widget * to Counter *

In addition, we are very happy to discover that as Base Class is made as a base class, it does not increase the size of the Widget- if the size of the Widget independent individual is. Yes, I know that I just told you that there is no Class size of 0, but- oh, that is not what I really mean. My truly means that there is no size of any Objects. The C standard specification is clear that the size of the "base-class ingredient" in a derived object can be 0. In fact, many compilers have developed a so-called "blank basic category optimization" [Note 4].

Therefore, if a Widget contains a Counter, the size of the Widget will be increased. Because Counter's Data Member belongs completely, not someone's base-class ingredients, so it must have a non-zero size. However, if widget is inherited from counters, the compiler can keep the size of the Widget in the original state. This fact is designed for the design of "memory use status very tightly category design," proposing a interesting rule: "Private inheritance" and "composite technology (Containment, Composition) can do the same For the purpose, try to choose "private inheritance". (Translation: This is observed with the "Effective C 2 / E" clause 42 of Scott Meyers. The last paragraph of this clause suggests that if "Private Inheritance" and "Composite Technology" can do the same purpose, try to Select composite technology. However, please note that this suggestion given here is premise.)

The final design is nearly perfect. It practices the efficiency requirements, provided that your compiler has the ability of "Empty Base Optimization", which is "inherited from counters", this fact does not increase the size of the following category . In addition, all Counter MEMBER FUNCTIONS must be inlin. Such design also practices safety requirements because counting motion is automatically handled by Counter Member functions, and those functions will be automatically called C calls, and the use of the Private inheritance mechanism prevents implicit transformation - "implicit transformation" allows Derived- Class Objects is processed as the base-class objects. (Well, I admit that it is not absolute security: Widget's authors may be ridiculous with a category other than Widget, which means that he may let widget inheritance from counter <<> gidget>. I am The attitude taken by the possibility is: not adding.) This design is easy to use to the customer, but some people may ignite, but it can be easier. Using the Private Inheritance Mechanism, howMany () will become Private in the derived category, so a USING DECLAration must be included in the derived category, so that HOWMANY () becomes public to be used by the customer:

Class Widget: private counter {

PUBLIC:

??? // Let HowMany become public

??? using counter :: howmany;

?

??? ..... // Widget's remaining part has not changed.

}

?

Class abcd: private counter {

PUBLIC:

??? // Let HowMany become public

??? using counter :: howmany;

?

??? ..... // The remaining part of ABCD has not changed.

}

For compilers that do not support Namespaces (namespace), the above purpose can also be done with old access hierarchies (but not encouraged):

Class Widget: private counter {

PUBLIC:

??? // Let HowMany become public

??? counter :: howmany;

?

??? ..... // Widget's remaining part has not changed.

}

At this point, it is necessary to perform the client programs that "object count", and the need to let the counter use the Classes used for its customers (which is a copy of the Class interface), you must do two things: Put the Counter as a base Class and let howMany () can be taken [Note 5].

However, the use of inheritance mechanisms can cause two cases worth noting. The first thing is ambiguity. Suppose we intend to count the Widgets, and we want this count value for general purpose. As previously displayed, we make Widget inherited from Counter and made widget :: howmany () becomlic. Now, we have a Class SpecialWidget, inheriting from Widget in public way, and we want to provide the SpecialWidget user as a function that Widget users can enjoy. No problem, just make the SpecialWidget inherited from the Counter. But there is a problem of Ambiguity. Which HOWMANY () is available for SpecialWidget for SPECIALWIDGET? Is that inherited from Widget, or inherited from the COUNTER? What we want, of course, is from counters, but we can't say our wish without expressing SpecialWidget :: Howmany (). Fortunately, it is just a simple inline function:

Class SpecialWidget: Public Widget,

??? ?????????????? private counter {

PUBLIC:

??? .....

?? static size_t howmany ()

??? {return counter :: howmany ();

??? .....

}

The second comment on the "use inheritance mechanism to complete the object count" is that the value of the Widget :: HowMany () is not only including the number of Widget Objects, but also the Objects generated by the Widget derived category. If the unique derivative category of Widget is SpecialWidget, there are five Widget independent objects and three SpecialWidgets independent objects, then widget :: howmany () will return 8. After all, the construction of each SpecialWidget also completed the construction of its basic category (Widget Ingredient).

Summary

The following points are what you need to remember:

· ????????? The automation of the object count work is not difficult, but it is not as simple as intuitive imagination. Use "Do IT for Me" Pattern (Coplien called "Curiously Recurring Template Pattern") It is possible to generate a correct number of counters. With the Private inheritance mechanism, you can provide object count capabilities, and no size of the object.

· ???????? When the customer has the opportunity to select "Inheritance from an Empty Class" or "Introduction to a Class Object as Data Member", inheritance is a better choice because it allows more tightly Object.

· ???????? Since the C does every effort to avoid memory vulnerability (Memory Leaks) when the HEAP Objects constructs actions, there is usually necessary to use the program code to use the Operator New. Operator delete.

· ???????? Counter Class Template does not care about whether you inherit it, or an object containing it. It looks the same. Therefore, the customer can freely select the "Inheritance Mechanism" or "Composite (Combination) Technology", even different strategies in different locations of the same application or library. Note and 考

[1] James O. Coplien. "The Column WITHOUT A Name: a curiously recurring template pattern," C Report, February 1995.

[2] Another way is to ignore widget :: howmany (), let the customer call counter :: howmany (). However, for this purpose, we will assume that we hope howmany () is part of the Widget interface.

[3] Scott Meyers. More Effective C (Addison-Wesley, 1996), PP. 113-122.

[4] Nathan Myers. "THE EMPTY MEMBER C Optimization," Dr. Dobb's Journal, August 1997. You can get: http://www.cantrip.org/emptyopt.html.

[5] As long as you do a simple change in this design, you can make widgets to calculate the number of objects in Counter, and do not allow this count value to be used by Widget's customers, and do not allow Counter :: Howmany () to call directly. The following this exercise leaves time a bad reader: continue to discuss more changes.

Further reading

If you want to learn more about New and Delete, please read Dan Saks in the column you hosted by Cuj from January 1997, or my more Effective C (Addison-Wesley, 1996). If you want to verify the object count (object-counting) problem, how to limit the number of times a Class is now, see More Effective C Terms 26.

Thank you

Mark Rodgers, Marco Dalla Gasperina, and Bobby Schmidt have some comments on the draft of this article. Their cave and proposal make this article a better improvement in many ways.

Author

Scott Meyers is the author of the bestseller Effective C Second Edition and More Effective C (two books are published by Addison Wesley). You can find more about him from http://www.aristeia.com/, his book, his dog's message. Translation: "Effective C " second edition is a cover with Meyers's dog.

Translator

Chen Jing, freelance writer, specialties C / Java / OOP / GP / DP. The textual expressions used to enthusiasm have a deep care of cold text.

Sidebar: Placement New with Placement Delete

The peer of malloc () in C is Operator New, free () peer in C is OPERATOR Delete. Unlike Malloc () and Free (), Operator New and Operator Delete can be overloaded, and the overloaded version can accept different numbers and different numbers of numbers of the master. This has always been correct for Operator New, but until recently, it is also true for Operator Delete. The normal tag of Operator New is:

Void * Operator new (SIZE_T) THROW (std :: bad_alloc);

(From now on, in order to simplify, I will deliberate the Exception Specifications (the translation: is the above ", because they have a close relationship with the focus I have to say.) Operator new overload version Can only increase the number of new numbers, so an operator new overload version may grow this:

Void * Operator New (size_t, void * wheretoputObject)

{RETURN WHERETOPUTOBJECT;

This special version of Operator New accepts an additional VOID * quoter indicating what indicators should be sent back. Since this special form is so common in the C standard library (declared in the header), there is a name belonging to your own: "Placement New". This name shows its purpose: allowing programmers to point out "An Object should be born in the memory."

Over time, any Operator New version of "Request additional quotes" has also gradually adopted this term for Placement New. In fact, this term has been remembered in the C standard specification. Therefore, when the C programmer talks about the so-called Placement New function, they may be that the above "requires an extra void * 叁 number, which is used to point out where the object is placed, but may also refer to those "The desired quota is more than a single_t quotient number of SIZE_T quotes", including the above functions, including Operator New Versions of other "quotes more".

In other words, when we focus on the memory configuration, "Placement New" means "a version of Operator New, accepts extra quotes". This term may have something else in other occasions, but we do not need to continue deep, so so far. If you need more details, please refer to the final examinations listed in this article.

Similar to Placement New, the term "Placement Delete" means "a version of Operator Delete, accepts extra quotes". The "normal" tag of Operator delete is as follows:

Void Operator delete (void *);

So, any version of Operator Delete, as long as the received quota is more than the VOID * above, is a Placement delete function.

Now let's re-return a topic discussed herein. What happens when Heap Object throws an exception during the construction period? Take this simple example again: Class ABCD {...};

ABCD * P = New Abcd;

Assume that an exception is caused when ABCD Object is generated. The main article of the prior column pointed out that if the exception comes from the ABCD construction, the Operator delete will automatically arly, release the memory configured by the Operator New. But if the operator new is loaded, what will be? What is the case in different ways of configuring a memory in different ways in different ways? How do Operator delete knows how to do the memory correctly? In addition, if the ABCD Object is generated in Placement New (like this below), what is:

Void * ObjectBuffer = getPoinTostostaticBuffer ();

Abcd * p = new (ObjectBuffer) Abcd; // Generate an ABCD Object in a static buffer

The above PLACEMENT NEW does not configure any memory. It just passes an indicator and points to the static buffer it accepts. Therefore, no release action is required.

Obviously, the actions taken by Operator Delete (used to reply to the behavior of its corresponding Operator New) must be determined by the Operator New version used when the memory is configured.

In order to make programmers have the opportunity to indicate how "How to reply to the Operator New behavior of a particular version", the C Standards Committee expands C , allowing Operator Delete to be multiped. When Heap Object's Constructor throws out an exception, the entire game will change another road, call the special Operator Delete version, this version has additional number of types, these types will correspond to Operator New previously version.

If there is no version of the additional number of Placement delete, it can correspond to the "extra number of Pablement New", then there will be no Operator delete being evoked. Thus, the impact of Operator New's behavior cannot be removed. This is no problem for those "Placement Edition", this is no problem because they don't really configure memory. However, in general, if you produce a custom "Placement version" Operator New, you should also generate a corresponding custom "Placement version" Operator Delete.

Ah, most compilers have not supported Placement Delete. The program code produced in this compiler makes you almost a memory vulnerability (Memory Leak). If there is an exception to be thrown out during the Heap Object Construction, because there will be no one attempt to release the constructor to be configured before being configured.

Translation: According to my test, GNU C 2.9, Borland C Builder 40, Microsoft Visual C 6.0 three compilers have supported the Placement version of Operator Delete. Among them, Visual C is the most thoughtful. When "There is no version of the additional number of Placement delete, Visual C will give you a warning message when the number of PfaceMent New's additional numbers are called: no matching operator delete Found; Memory Will NOT BE FREED INITIALIZATION THROWS AN Exception. • Articles

Counting Objects in C

Scott Meyers

It isn't hard to keep a count of all the objects allocated for a Given Class In C , unlesss you have to deal with distracts.

Sometimes easy things are easy, but they're still subtle. For example, suppose you have a class Widget, and you'd like to have a way to find out at run time how many Widget objects exist. An approach that's both easy to implement and that gives the right answer is to create a static counter in Widget, increment the counter each time a Widget constructor is called, and decrement it whenever the Widget destructor is called. You also need a static member function howMany to report how many Widgets Currently Exist. if Widget Did Nothing But Track How Many of Its Type Exist, It Would Look More Or Less Like this:

Class widget {

PUBLIC:

Widget () { count;}

Widget (const widget &) { count;}

~ Widget () {--count;}

Static size_t howmany ()

{return count;}

Private:

Static size_t count;

}

// Obligatory definition of count. this

// Goes in an importation file

SIZE_T WIDGET :: count = 0;

This works fine. The only mildly tricky thing is to remember to implement the copy constructor, because a compiler-generated copy constructor for Widget would not know to increment count.If you had to do this only for Widget, you'd be done , but counting objects is something you might want to implement for several classes. Doing the same thing over and over gets tedious, and tedium leads to errors. to forestall such tedium, it would be best to somehow package the above object-counting code so IT Could Be Reused in Any Class That Wanted It. The Ideal Package Would:

Be Easy to Use - Require Minimal Work On The Part of Class Authors Who Want To Use It It. ideally type "be efficient -. impose no unnecessary space or time penalties on client classes employing the package be foolproof -. be next to impossible to accidently yield a count that is incorrect (We're not going to worry about malicious clients, ones who. Delibely try to Mess Up The Count. in C , Such Clients Can Always Find A Way to Do Their Dirty Deeds.)

Stop for a moment and think about how you'd implement a reusable object-counting package that satisfies the goals above. It's probably harder than you expect. If it were as easy as it seems like it should be, you would not be reading An Article About It in this magazine.

New, Delete, And Exceptions

While you're mulling over your solution to the object-counting problem, allow me to switch to what seems like an unrelated topic. That topic is the relationship between new and delete when constructors throw exceptions. When you ask C to dynamically allocate an object , you use a new expression, as in: class abcd {...}; // abcd = "a big complex datatype"

Abcd * p = new abcd; // a new expression

The new expression - whose meaning is built into the language and whose behavior you can not change - does two things First, it calls a memory allocation function called operator new That function is responsible for finding enough memory to hold an ABCD object If the... Call to operator new successs, the new expression.

But suppose operator new throws a std :: bad_alloc exception. Exceptions of this type indicate that an attempt to dynamically allocate memory has failed. In the new expression above, there are two functions that might give rise to that exception. The first is the invocation of operator new that is supposed to find enough memory to hold an ABCD object. The second is the subsequent invocation of the ABCD constructor that is supposed to turn the raw memory into a valid ABCD object.

If the exception came from the call to operator new, no memory was allocated. However, if the call to operator new succeeded and the invocation of the ABCD constructor led to the exception, it is important that the memory allocated by operator new be deallocated. . If it's not, the program has a memory leak It's not possible for the client - the code requesting creation of the ABCD object - to determine which function gave rise to the exception.For many years this was a hole in the draft C language specification , but in March 1995 the C Standards committee adopted the rule that if, during a new expression, the invocation of operator new succeeds and the subsequent constructor call throws an exception, the runtime system must automatically deallocate the memory that operator new allocated. This deallocation Is Performed by Operator Delete, The Deallocation Analogue of Operator New. (for Details, See The Sidebar On Placement New and Placement Delete.)

IT IS This Relationship Between new Expressions and operator delete affects us in Our attempt to automate the counting of object instantiations.

Counting Objects

. In all likelihood, your solution to the object-counting problem involved the development of an object-counting class Your class probably looks remarkably like, perhaps even exactly like, the Widget class I showed earlier:

// See Below for a Discussion of Why

// this isn't Quite Right

Class counter {

PUBLIC:

Counter () { count;}

COUNTER (const counter &) { count;}

~ Counter () {--count;

Static size_t howmany ()

{return count;}

Private:

Static size_t count;

}

// this STILL GoES in AN AN

// Implementation File

SIZE_T Counter :: count = 0;

The idea here is that authors of classes that need to count the number of objects in existence simply use Counter to take care of the bookkeeping. There are two obvious ways to do this. One way is to define a Counter object as a class data member , AS in: // Embed a counter to count objects

Class widget {

PUBLIC:

..... // all the usual public

// Widget Stuff

Static size_t howmany ()

{Return Counter ::howMany ();

Private:

..... // all the usual private

// Widget Stuff

Counter C;

}

The Other Way Is To Declare Counter As A Base Class, AS IN:

// inherit from counter to count Objects

Class Widget: public counter {

..... // all the usual public

// Widget Stuff

Private:

..... // all the usual private

// Widget Stuff

}

Both approaches have advantages and disadvantages. But before we examine them, we need to observe that neither approach will work in its current form. The problem has to do with the static object count inside Counter. There's only one such object, but we need one for each class using counter. for example, if we want to count both Widgets and ABCDs, we need two static size_t objects, not one. Making counter :: count nonstatic does not solve the problem, because we need one counter per class, Not One Counter Per Object.

We can get the behavior we want by employing one of the best-known but oddest-named tricks in all of C : we turn Counter into a template, and each class using Counter instantiates the template with itself as the template argument.

Let Me Say That Again. Counter Becomes a Template:

Template

Class counter {

PUBLIC:

Counter () { count;}

COUNTER (const counter &) { count;}

~ Counter () {--count;

Static size_t howmany ()

{return count;} private:

Static size_t count;

}

Template

Size_t

Counter :: count = 0; // this now can go in header

The First Widget Implementation Choice Now Looks Like:

// Embed a counter to count Objects

Class widget {

PUBLIC:

.....

Static size_t howmany ()

{Return Counter ::howMany ();

Private:

.....

Counter C;

}

And The Second Choice Now Looks Like:

// inherit from counter to count Objects

Class Widget: public counter {

.....

}

NOTICE HOW in Both Cases We Replace Counter with counter, Each Class Using Counter Instantiates The Template with Itself as the argument.

The tactic of a class instantiating a template for its own use by passing itself as the template argument was first publicized by Jim Coplien. He showed that it's used in many languages ​​(not just C ) and he called it "a curiously recurring template pattern" [1]. I do not think Jim intended it, but his description of the pattern has pretty much become its name. That's too bad, because pattern names are important, and this one fails to convey information about what it does or how it's Used.

THE Naming of patterns is as much art as anything else, and i'm not very good at it, but I'd probably call this pattern something like "do it for me." Basical, Each Class Generated from Country Provides a Service IT Counts How Many Objects Exist) for the Class Requesting The Counter Instantiation. so The class counter counter widgets, and the class counter counts abcds.

Now that Counter is a template, both the embedding design and the inheritance design will work, so we're in a position to evaluate their comparative strengths and weaknesses. One of our design criteria was that object-counting functionality should be easy for clients to obtain, and the code above makes clear that the inheritance-based design is easier than the embedding-based design. that's because the former requires only the mentioning of Counter as a base class, whereas the latter requires that a Counter data member be defined and that howMany be reimplemented by clients to invoke Counter's howMany [2]. that's not a lot of additional work (client howManys are simple inline functions), but having to do one thing is easier than having to do two. So let's first turn our attention To the design employing inheritance.using public inheritance

The design based on inheritance works because C guarantees that each time a derived class object is constructed or destroyed, its base class part will also be constructed first and destroyed last. Making Counter a base class thus ensures that a Counter constructor or destructor will be called Each Time a class inheriting from it has an object created or destroyed.

Any time the subject of base classes comes up, however, so does the subject of virtual destructors. Should Counter have one? Well-established principles of object-oriented design for C dictate that it should. If it has no virtual destructor, deletion of A Derived Class Object Via A base Class Pointer Yields undefined (and type) Results:

Class Widget: Public Counter

{...};

Counter * Pw =

New widget; // get base class Ptr

// to Derived Class Object

......

Delete Pw; // Yields undefined resultS // if the base class lacks

// a Virtual Destructor

Such behavior would violate our criterion that our object-counting design be essentially foolproof, because there's nothing unreasonable about the code above. That's a powerful argument for giving Counter a virtual destructor.

Another criterion, however, was maximal efficiency (imposition of no unnecessary speed or space penalty for counting objects), and now we're in trouble. We're in trouble because the presence of a virtual destructor (or any virtual function) in Counter means each object of type Counter (or a class derived from Counter) will contain a (hidden) virtual pointer, and this will increase the size of such objects if they do not already support virtual functions [3]. That is, if Widget ITSelf Contains No Virtual Functions, Objects of Type Widget Would Rerease In Size IF Widget Started Inheriting from Counter. We don't want.

The only way to avoid it is to find a way to prevent clients from deleting derived objects via class base class pointers It seems that a reasonable way to achieve this is to declare operator delete private in Counter.:

Template

Class counter {

PUBLIC:

.....

Private:

Void Operator delete (void *);

.....

}

Now The Delete Expression Won't compile:

Class Widget: Public Counter {...};

Counter * pw = new widget; ......

Delete Pw; // Error. Can't Call Private

// Operator Delete

Unfortunately - and this is the realin't Compile EIG!

Counter * Pw =

New widget; // this stay NOT

// compile because

// Operator Delete IS

// private

Remember from my earlier discussion of new, delete, and exceptions that C 's runtime system is responsible for deallocating memory allocated by operator new if the subsequent constructor invocation fails. Recall also that operator delete is the function called to perform the deallocation. But we've declared operator delete private in Counter, which makes it invalid to create objects on the heap via new! Yes, this is counterintuitive, and do not be surprised if your compilers do not yet support this rule, but the behavior I've described is correct. Furthermore, there's no other obvious way to prevent deletion of derived class objects via Counter * pointers, and we've already rejected the notion of a virtual destructor in Counter. So I say we abandon this design and turn our attention to using A counter data member instead.

USING a DATA MEMBER

We've already seen that the design based on a Counter data member has one drawback:. Clients must both define a Counter data member and write an inline version of howMany that calls the Counter's howMany function That's marginally more work than we'd like to Impose On Clients, But it's hardly unmanageable. there. The addition of a counter. The address of a counter ince..................

AT First Blush, This Is Hardly A Revelation. After All, How Surprising Is It That A Data MEMBER THAT THAT Makes Objects of That Type BIGGER? But Blush Again. Look at The Definition of Counter:

Template

Class counter {

PUBLIC:

Counter ();

Counter (Const Counter &);

~ Counter ();

Static size_t howmany ();

Private:

Static size_t count;

}

Notice how it has no nonstatic data members. That means each object of type Counter contains nothing. Might we hope that objects of type Counter have size zero? We might, but it would do us no good. C is quite clear on this point. All objects have a size of at least one byte, even objects with no nonstatic data members. By definition, sizeof will yield some positive number for each class instantiated from the Counter template. So each client class containing a Counter object will contain more data than it would if it did not contain the Counter. (Interestingly, this does not imply that the size of a class without a Counter will necessarily be bigger than the size of the same class containing a Counter. that's because alignment restrictions can enter into the matter. For example, if Widget is a class containing two bytes of data but that's required to be four-byte aligned, each object of type Widget will contain two bytes of padding, and sizeof (Widget) will return 4. If, as is Common, C ompilers satisfy the requirement that no objects have zero size by inserting a char into Counter, it's likely that sizeof (Widget) will still yield 4 even if Widget contains a Counter object. That object will simply take the place of one of the bytes of padding That Widget Already Contained. this is not a Terribly Common Scenario, However, And We Certainly Can't Plan ON IT WHENDAING A WAY to PACKAGE Object-Counting Capabilities.)

I'm Writing this at the Very Beginning of The Christmas Season. (IT IS in Fact Thanksgiving Day, Which Gives You Some Idea of ​​How I Celebrate Major Holidays ...) Already I'm in a bah humbug mood. All i want To do is count Objects, and i don't want to do it. There Has got to be a way.using private inheritance

Look Again at the inheritance-based code That LED to the NEED TO Contem A Virtual Destructor in Counter:

Class Widget: Public Counter

{...};

Counter * Pw = new widget;

......

Delete

PW; // Yields undefined results

// if counter lacks a Virtual

// deStructor

Earlier we tried to prevent this sequence of operations by preventing the delete expression from compiling, but we discovered that that also prohibited the new expression from compiling. But there is something else we can prohibit. We can prohibit the implicit conversion from a Widget * pointer (Which is what new returns) to a counter * Pointer. in Other Words, We can prevent inheritance-based Pointer Conversions. All we have to do is replace the use of public inheritance with private inheritance:

Class Widget: Private Counter

{...};

Counter * Pw =

New widget; // error! No iMPlicit

// converness from

// widget * to

// counter *

. Furthermore, we're likely to find that the use of Counter as a base class does not increase the size of Widget compared to Widget's stand-alone size Yes, I know I just finished telling you zero that no class has size, but - well, that's not really what I said. What I said was that no objects have zero size. The C Standard makes clear that the base-class part of a more derived object may have zero size. In fact many compilers implement what has come to be known as the empty base optimization [4] .Thus, if a Widget contains a Counter, the size of the Widget must increase. The Counter data member is an object in its own right, hence it must have nonzero size. But if Widget inherits from Counter, compilers are allowed to keep Widget the same size it was before This suggests an interesting rule of thumb for designs where space is tight and empty classes are involved:. prefer private inheritance to containment when both will do.

This last design is nearly perfect. It fulfills the efficiency criterion, provided your compilers implement the empty base optimization, because inheriting from Counter adds no per-object data to the inheriting class, and all Counter member functions are inline. It fulfills the foolproof criterion , because count manipulations are handled automatically by Counter member functions, those functions are automatically called by C , and the use of private inheritance prevents implicit conversions that would allow derived-class objects to be manipulated as if they were base-class objects. (Okay , it's not totally foolproof:. Widget's author might foolishly instantiate Counter with a type other than Widget, ie, Widget could be made to inherit from Counter I choose to ignore this possibility) The design is certainly easy for clients to use, but some. May Grumble That It Could Be Easier. The Use of Private Inheritance Means That Howiver Will Become Private in Inheriting Classes, So Such Classes Must Include a Using Declaration to makeh howmany public to their clients:

Class Widget: private counter {

PUBLIC:

// Make HowMany Public

USING Counter :: HOWMANY;

.....// REST OF Widget is Unchanged

}

Class abcd: private counter {

PUBLIC:

// Make HowMany Public

USING Counter :: HOWMANY;

.....// REST OF ABCD IS Unchanged

}

For Compilers NOT Supporting Namespaces, The Same Thing IS Accomplished by Replacing The Using Declaration with The Older (now Deprecated) Access Declaration:

Class Widget: private counter {

PUBLIC:

// Make HowMany Public

Counter :: howmany;

.....// REST OF Widget is Unchanged

}

Hence, clients who want to count objects and who want to make that count available (as part of their class's interface) to their clients must do two things: declare Counter as a base class and make howMany accessible [5] .The use of inheritance does, however, lead to two conditions that are worth noting. The first is ambiguity. Suppose we want to count Widgets, and we want to make the count available for general use. As shown above, we have Widget inherit from Counter and we make howMany public in Widget. Now suppose we have a class SpecialWidget publicly inherit from Widget and we want to offer SpecialWidget clients the same functionality Widget clients enjoy. No problem, we just have SpecialWidget inherit from Counter.

But here is the ambiguity problem. Which howMany should be made available by SpecialWidget, the one it inherits from Widget or the one it inherits from Counter? The one we want, naturally, is the one from Counter, but there's no way to say that WITHOUT ACTUALLY WRITINGTUALWIDGET :: HOWMANY. Fortunately, IT's a Simple Inline Function:

Class SpecialWidget: Public Widget,

Private counter {

PUBLIC:

.....

Static size_t howmany ()

{Return Counter ::howMany ();

.....

}

The second observation about our use of inheritance to count objects is that the value returned from Widget :: howMany includes not just the number of Widget objects, it includes also objects of classes derived from Widget. If the only class derived from Widget is SpecialWidget and there are five stand-alone Widget objects and three stand-alone SpecialWidgets, Widget :: howMany will return eight. After all, construction of each SpecialWidget also entails construction of the base Widget part.

Summary

The following points are really all you need to remember:. Automating the counting of objects is not hard, but it's not completely straightforward, either Use of the "Do It For Me" pattern (Coplien's "curiously recurring template pattern") makes it possible to generate the correct number of counters. The use of private inheritance makes it possible to offer object-counting capabilities without increasing object sizes. When clients have a choice between inheriting from an empty class or containing an object of such a class as a data member, inheritance is preferable, because it allows for more compact objects. Because C endeavors to avoid memory leaks when construction fails for heap objects, code that requires access to operator new generally requires access to the corresponding operator delete too. The Counter class template doesn 'T Care WHETHER You Inherit from Its Type. It Looks The Same Regardless. Hence, Clients Can Freely Choose Inheritance or Conta Inities, Even Using Different Strategies in Different Parts of a Single Application Or Library. O

Notes and References

[1] James O. Coplien. "The Column WITHOUT A Name: a curiously recurring template pattern," C Report, February 1995.

.

[3] Scott Meyers. More Effective C (Addison-Wesley, 1996), PP. 113-122.

[4] Nathan Myers. "THE EMPTY MEMBER C Optimization," Dr. Dobb's Journal, August 1997. Also Available At http://www.cantrip.org/emptyopt.html.

[5] Simple variations on this design make it possible for Widget to use Counter to count objects without making the count available to Widget clients, not even by calling Counter :: howMany directly Exercise for the reader with too much free time:. Come up With OR More Such Variations.

Further Reading

To learn more about the details of new and delete, read the columns by Dan Saks on the topic (CUJ January - July 1997)., Or Item 8 in my More Effective C (Addison-Wesley, 1996) For a broader examination of the Object-Counting Problem, Including How To Limit The Number of Instantiations of A Class, Consult Item 26 of More Effective C .

Acknowledgments

Mark Rodgers, Marco Dalla Gasperina, and Bobby Schmidt Provided Comments on Drafts of this Article. Their Insights and suggestions improved it in Several Ways.

Scott Meyers authored the best-selling Effective C , Second Edition and More Effective C (both published by Addison Wesley). Find out more about him, his books, his services, and his dog at http://www.aristeia.com.

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

New Post(0)