Trinity

zhaozj2021-02-12  142

Trinity

Trinity is a more famous word in English. It expressed three separate people as one in the theology, the Father, Son, and Holy God synthesis. There is also a TRINITY in C , which is best to appear together to avoid possible errors. That is the copy constructor, assignment operator (Operator =), and destructor. The three either appear simultaneously, or disappear [Note 1] at the same time. If you have never heard of this proposal, you can refer to the "C Made Easier: The Rule of Three" written by Andrew Koenig and Barbara E. Moo in "C / C User" in 2001.6 months.

Note 1: There is always an exception, nothing wrong, in general, with copy constructors, the DESTRUctor, but in turn, there is not necessarily To have a copy operation.

For a destructuring function as a base class, if this base class has a polymorphic deletion, the parsing function should be PUBLIC Virtual, and the patriability function should be protected by Protected Non-Virtual.

Generally, this is because the structure, copy, sectations will allocate and release resources. But this is not the focus I have to say today, and the code extracted from the above article describes everything.

// There is a wrong code

// this class contains a subtle error

Class IntVec {

PUBLIC:

INTVEC (INT N): DATA (new int in) {}

~ IntVec () {delete [] data;};

INT & Operator [] (int N)

{Return Data [N];

Const Int & Operator [] (INT N) Const

{Return Data [N];

Private:

INT * DATA;

}

// violence corrections, absolutely prohibiting copy

// this class corrects the error

// By Brute Force

Class IntVec {

PUBLIC:

INTVEC (INT N): DATA (new int in) {}

~ IntVec () {delete [] data;};

INT & Operator [] (int N)

{Return Data [N];

Const Int & Operator [] (INT N) Const

{Return Data [N];

Private:

INT * DATA;

// There Two Member Functions Added

INTVEC (Const IntVec &);

INTVEC & OPERATOR = (Const IntVec &);

}

// Correct, plus copy operation

// this class corrects the error by

// defining Copying and Assignment

Class IntVec {

PUBLIC:

INTVEC (INT N): DATA (new int in), size (n) {}

~ IntVec () {delete [] data;};

INT & Operator [] (int N)

{RETURN DATA [N];} const INT & Operator [] (int N) const

{Return Data [N];

IntVec (Const IntVec & V):

Data (new int [v.size]),

Size (v.size) {

Std :: Copy (Data, Data Size, V.DATA);

}

IntVec &

Operator = (const INTVEC & V) {

INT * newdata = new int [v.size];

Std :: Copy (v.data, v.data v.size, newdata);

DELETE [] DATA;

Data = newdata;

Size = v.size;

Return * this;

}

Private:

INT * DATA;

Int size;

}

This general three-in one must first understand.

If you use the PIMPL technique for an exception security [Note 2]. Among them, the intelligent pointer auto_ptr is used.

Note 2: You can refer to the "More Exceptional C " terms of Herb Sutter 22 "Design of Exceptional and Class", PIMPL techniques should first appear in "Exceptional C ", but I don't have this book, readers can go to the reference Related Terms. The method of the method is very simple, and the substance is hidden in Class Implement, and then points it with an outsourced auto_ptr.

The cargill widget example in "MEC" is borrowed here.

Class Widget

{

PUBLIC:

Widget (); // initializes pimpl_ with new widgetImpl

~ Widget (); //must be provided, Because the Implicit

// Version Causes Usage Problems

// (See Items 30 and 31)

Widget & Operator = (Const Widget &);

// ...

Private:

Class WidgetImpl;

Auto_ptr pimpl_;

// ... Provide Copy Construction That

// Works Correctly, Or Suppress IT ...

}

// Then, Typically in a Separate

// Implementation File:

//

Class Widget :: WidgetImpl

{

PUBLIC:

// ...

T1 T1_;

T2 T2_;

}

The purpose of Auto_PTR is to automatically manage memory resources. The above class has only one data member (Data Member) PIMPL_, so it is not necessary to use the destructor. Automatically generated destructor is sufficient.

But we often watch turbid water and fascinated in Qingyuan. Note that the explanation of the parallelism functions "The destructor must be provided, the version implicitly generated will cause the use problem." What is the problem? The explanation on the book is: "If the destructor is used automatically generated, the destructor will be defined in each compile unit, thus, the definition of WidgetImpl must be visible at each compiler."

This explanation flashes his words, and the language is unknown. I remember that I was very confused when I saw it, and I used it in the afternoon to understand. In this type of design, the Copy Constructor and Assignment Operator provide apparent, for the normal transfer of resources, don't say more. But even if the destructor is empty, it is necessary to provide it. Why?

For better hidden information, a simple answer. In general, when we design the class, it will be implemented with .H and .CPP files separately. H and .cpp files respectively. When compiling, other files are used, just need to include it .h header file, without tube .cpp implement file, connection (link) will naturally be found. Such modularization is extremely frequently used in C / C languages, which has achieved separation of interface (a set of services) and implementation (completion of service functions).

The definition of Class Widget :: Widget :: WidgetiMPL in the above example is definitely in a separate file, just like its name, is not required to be seen by the customer (Client).

If we provide a destructor for class widget, even if it is empty, it is inline, and we compiled in the code as a function existing as a function. But in contrast, if we do not define a parsive function, you will generate a destructor in the file (in PALCE) in the file (Note 3); if you use this destructure in multiple files The function will generate such a destructor in multiple files, just like the "Matrix II" in the production of fire-born Agent SMith :)

Note 3: Note Be sure, if it is the same value like old-fashioned C struct, that is, the general POD Data Struct, the destructor is not used, and it is of course not generated. come out. But the member data here is an auto_ptr , which has a nonTrivial destructor, so the destructive function of the host type Widget cannot be ignored. It must be generated when used.

The problem we face now is here. Discussions in two cases:

1. For the normal defined destructor, in the file used in this destructor, it will really call it (call) it, just like calling a normal member function, not to generate itself. This file will definitely contain this class .h declaration file, of course, there will be this class destructor declaration (declaration), and then determine member functions by the connector (Linker) after compilation (LINK) Address (of course, also includes a destructive function), in order to dynamically call it during operation.

2. However, for the destructor without declaration and definition, it needs to be generated by the compiler. It has been clearly understood, generated in the use of the file, because it does not have a fixed location, so you can include This class is. H declaration file, it is impossible to have the address of the destructive function when the connection is connected (it does not exist). In the above example, since the Widget's member data AUTO_PTR destructor needs to be defined by the WidgetImpl class, in order to generate a suitable Widget function, you must know the full definition of WidgetImpl, so the full definition of WidgetImpl must be Widget's .h declaration file, the WidgetImpl is a related information that is related to implementation, which is generally placed in a separate file, so if you don't want to write a function, you must expose the implementation of WidgetImpl. The entire derivation process, the key is to carefully think about the compilation and connection process of the C program, plus the knowledge of a little compilation principle, can be suddenly turned over.

Conclusion: You still have to write the patterned function, even if it is empty, you must pay the cost of exposure, this price seems too much :)

For the situation where the class itself has resource allocation and release, whether it is the class itself, or by the member data of the class, most of the cases we must provide Copy Constructor and Assignment Operator (or sometimes clear) It is forbidden to provide a destructor.

So our ancient principles Trinity, even if there is a smart pointer, even if the destructor is empty, it should still be observed.

Wu Tong wrote in 2003.5.31

Recently modified 2003.6.16

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

New Post(0)