"Meditation on C + +": A Checklist for class designers--11 questions about class

Source: Internet
Author: User

The 11 questions in this paper are taken from the fourth chapter of the meditation record of C + +. The description of all the questions is self-supplementing.

1 Does your class need a constructor?

--correctly define the constructor, and grasp the functional scope of the constructor function

    • Some classes are too simple, their structure is their interface, so constructors are not required.
class print{    void print1(){cout<<"1"<<endl;}    void print2(){cout<<"2"<<endl;}    void print3(){cout<<"3"<<endl;}};
    • Some classes are complex and the construction process has complex behavior, so you must design a constructor, or even multiple overloaded constructors.
string{public:    stringnewchar [15];}    string(conststring &);    string(constchar *);private:    char *_buf_ptr;};
    • I am often troubled by the question of:
      "Should I put this part of the operation in a constructor, or would I declare another function to execute it separately?" "
      For example:
class socket_connect{public:    socket_connect();    PowerOn();private:    int sock_fd;};socket_connect::socket_connect(){    sock_fd = socket(AF_INET,SOCK_STREAM,0);    init_sockaddr_in();}void socket_connect::PowerOn(){    int ret = bind(sock_fd,(const sockaddr*)&servaddr,sizeof(servaddr));    PERROR(ret,"bind failed!");    ret = listen(sock_fd,BACK_LOG);    PERROR(ret,"listen failed!");}

Here, according to our needs, the socket itself should start listening at the time of initialization? So we can simply put bind() and listen() put it in the constructor!

But I'm still proposing a function alone PowerOn() , which is justified. Because I prefer to create a socket as a global variable, and I don't want the client trying to connect before I can finish initializing a series of sockets.

Well, it all sounds reasonable. The first approach gives the constructor a more comprehensive "construct" function, and the second approach refines the operation to better control the behavior of the class.

    • So. Which step does the constructor take?

      1. One argument is that a PowerOn() single init() function like this can easily be forgotten, and we should let the class run happily after it is constructed!
      2. Another voice says: Too complex a constructor is unreliable! You shouldn't trust the constructor too much! If you have a problem calling the constructor, the consequences will be serious!
    • At the moment. I haven't found a strict limit yet. But for complex classes, the following two must not be wrong!
      -empty constructors are stupid behavior! You should at least assign the data to 0.
      -a constructor that implements too much versatility is also a foolish act! Overly complex constructors can make the class look messy
      This extra piece is not a sophisticated one:
      -If there is init() such a function as a complement to the constructor, at least it should be guaranteed that when I forget to call, I init() will give a warning or a reminder in some way!

2 are your data members private?

- pubic - private or what protected ?

In the process of using C + + more and more, I tend to hide all data members in such a way. Look at an example like this:

classsource{ Public:voidInitConst Char*,unsigned int,unsigned int,void*);inline voidIncrease () {source_amount++;};inline intDecrease (unsigned intVal) {Source_amount-= val};unsigned intGet_sleeptime ()Const{returnSleep_time;}unsigned intGet_amount ()Const{returnSource_amount;}unsigned intGet_speed ()Const{returnSource_speed; }Char* Get_name ()Const{returnName }Private: Mutex Source_lockCharname[ -];unsigned intSource_amount;unsigned intSleep_time;unsigned intSource_speed;};

See! I've set all the data members ' access rights to private!
If you are lazy. Exposing data members directly to public is not a bad idea, but there are two fatal drawbacks

    • loss of complete control over changes to data members
      . We don't know where, where, data members have been modified! Exposing data members means that you would have wanted to do a + + operation, but accidentally cleared 0 of the similar problem!
      . So, this is the equivalent of you give the root authority to all users, how horrible one thing!

    • not easy to modify
      . Hypothesis: Today our demand is: Each time this thing +1, tomorrow demand may become: every time this thing -1!
      . Do you mean that every time you have to change + + to all the places where you can change the class members? This is obviously unrealistic!
      . If we hide the data members and simply provide a single access interface, it's easier! I just need to change the behavior of this function!
      . In the example above, I can do and operate within the mutex_lock() interface mutex_unlock() , modifying the thread-safe self-increment operation! This is cool!

So hiding all the data members you want to protect can be a good habit! (although defining various function interfaces that replace access behavior increases the workload)

3 Does your class need a parameterless constructor?

This problem is similar to question 1 , and how far does the constructor function?
Obviously this question has no standard answer, the determinant is: your design intention to this class !

class Example{    Example(int p,int q){cout<<p+q<<endl;}};class Example_2{    Expamle_2() {cout<<"nothing"<<endl;}};

What are the losses if you supply only one constructor with parameters?

Example eg;Example_2 eg_2;
    • Obviously the first one is wrong. As the designer of the class, you know everything about this class and probably won't make such a mistake. But what if someone else needs to use your class? The creation of such objects clearly poses some minor problems.
    • Consider Example a[100] , in the same way, providing only one parameter constructor can sometimes lead to minor problems.

Well, what should I do?

    • If your design intent is clear: I don't want the compiler to create a default constructor for me, and I want to tightly control the constructor behavior of this class object.
    • Then rest assured that only provide a parametric constructor, it must be true! (but notice that the request is communicated to the user)
    • If you think about it, you feel that providing an parameterless constructor is not rejected by your design intent, and does not cause the program to crash because it does not assign a meaningful initial value to the object
    • Then providing a parameterless constructor will make your class easier to use!

Look at the string s; string s(const string&); STL.
This is a good reference model.

    • In addition, a parameter constructor that provides a default value can also be used for an argument-free invocation! and that's a good solution.
class A{public:    10) {}};...int main(){    A a(11);//合法    A b;//合法}
4 is the constructor supposed to initialize all data members?

It seems strange that the question should be answered in the following way:

Should every data member be initialized by a constructor function?

It is clear that a reasonable initialization should be done for each member that appears. Otherwise, undefined behavior can cause unexpected errors in the program.
Several common examples are:
int data = 0;
char *ptr = NULL;
I think most people know it like this to avoid an "uninitialized" Error! So it should be the case in the constructor.

However, do not do so absolutely. The book mentions:

Sometimes, classes have data members that make sense only after their objects have been there for a certain amount of time.

Do you want to initialize this type of member? This needs to be left to the actual problem to think about! However, the habit of initializing the pointer to null must not be wrong!

Do class 5 require destructors?

This question is not difficult to answer, I often do in the destructor is to use delete to release new the created object. ( malloc and free so is the same!) )

Make sure a new match is delete not too much and not less.
Sometimes the only thing to keep in mind is whether to use delete or delete [] .

Does the 6 class require a virtual destructor?

This question is more meaningful than question 5 !
The first thing you should know:

A class that is never used as a base class is not required by a virtual destructor!

So why do destructors sometimes need to be declared as virtual?
When should the destructor be declared virtual?

class B {};class D: public B {};int main(){    new//这是正确的    delete//可能会造成错误}

You should add a virtual destructor to B whenever someone might execute a delete expression on a pointer that actually points to a base class D object, but a type that does b* type!

Why did you do that? This is my interpretation.

    • First of all, you must know that there is a complete base class object inside the derived class, and all the changes made by the derived class are added after the base class object.
    • It can be understood that the base class B is 1 floor , the derived class D is 1 floor +2 floor. Now our pointer is B *b_ptr , it has a range of only 1 floor, but because B *b_ptr = new D; , so this pointer actually pointed to the object has 1 floor +2 floor .
    • If we delete b_ptr , obviously, because the pointer is limited by the compiler to the scope of 1 floor, so it will only be 1 floor was delete ! But don't forget, our target is 2 floor!
    • So what happens to the 2 floor when the 1 floor is torn down? Of course it is floating directly! No doubt, this is quite dangerous! The remaining 2 floors were not only released safely, but also because the 1 floor had disappeared, making it impossible for us to go through 1 upstairs to the 2 floor.

Therefore, the virtual destructor is necessary! For an improper use of the pointer (above b_ptr ), once we find that the pointer is unreasonable, by the way of dynamic binding, give him a false floor let it go to the destruction. This avoids the 2 floor on the 1 floor but the situation is not!

The virtual destructor is usually empty

7 Does your class need an assignment operator?

The equals sign = is so common that we often forget to make sure that class has = operations to use it.
What is the consequence of this?

class A{public:    Anewchar[100];}    ~A(){delete [] name;}//注意这里private:    char *name;};void function(constbase){    base;}//function结束后调用析构函数int main(){    base;    function(base);}//main结束后调用析构函数

In the above code, function() within the function, a named object is created copy and the operation is used = . What happens when you try to run it?

———————— Crash!!!

Why?

    • Because the default = operator simply assigns a copy of the memory. copyand base The data member char *name values are equal, pointing to the same piece of memory!
    • This causes that after the function() call is finished, the copy memory that is name pointed to has been deleted.
    • After the main function is finished, it needs to be refactored once base . And now the name has been released! Again delete must crash!

The revelation to us:

    • Dynamically allocated resources, be sure to consider overloading = .
      Here you can modify this:
A& A::operator=(const A&base){    charnewchar [100];    strcpy(temp,base.name);    name = temp;    //others = base.others;    return *this;}
    • Good pointer habits will allow you to avoid program crashes when the class design is imperfect!
    ~A(){delete [] name;name = NULL;}

-- reject the dangling pointer, start from me!

The above operation does not achieve the intended purpose, but at least it will not crash!

TIPS:

Typically operator= a reference class& should be returned, and by the return *this end to ensure consistency with the built-in replication operators.

8 Does your class need a copy constructor?

If you need a copy constructor, you probably need to define one too = . Even if you don't need one = , the way they're implemented is consistent. So see question 7

(Of course, if your copy constructor is designed to be good enough, you can use it instead of implementing it copy constructor operator= = copy constructor )

9 Does your assignment operator correctly assign an object to the object itself?

--Copy the trouble you bring

Remember the first question from the point of the sword?

Add an assignment operator function for this class, as in the declaration of type a below.

class A{public:    A(char *pData = NULL);//pData一般用new动态分配    A(const A&str);    ~A();private:    char *pData;};

Do you remember the question 7,8?

Our idea is clear: within the assignment operator function, call Delete, release the original string, and then new a string, with a strcpy copy! ( again: It is a good habit to assign null first after delete )

So think about it, what happens in the following statement?

A origin;origin = origin;

Origin executes the delete and then copies itself. Obviously, Origin played off! Copy your own time, has been delete, obviously can not complete the correct assignment!

The core problem is this--to prevent copying itself!

The solution is as follows:

A& A::operator =(const A& origin){    if(this==&origin)        return *this;    delete [] pData;    pData = NULL;    newchar [strlen(origin.pData)+1];    strcpy(pData,origion.pData);    return *this;}

Another feasible and better option is to temporarily save the value of origin with temp. (Swap statement order)

A& A::operator =(const A& origin){    charnewchar[strlen(origion.pData)+1];    strcpy(temp,origin.pData);    delete [] pData;    pData = temp;    return *this;}

Now that we've talked about this, I'll also mention the hole in copy constructor-which of the following copy constructors is correct?

class A{public:    A(A origion);//1    A(A& origion);//2    A(const A& origion);//3};
    • The first pass compiles!

      A's copy constructor prohibits parameters with and type A!
      This is a question worth thinking about. A chicken or an egg first ...

    • The second one can be compiled, but not good, the next question will be explained in detail.

    • The third one is recommended!
Ten const is always important!

At the end of question 9 , something about using the const qualifier in the assignment operator and the copy constructor has been mentioned.

We use the const to prevent the variable from being modified

In principle, all functions that do not wish to change data members are declared as const by us.

Then, when you really use the Const qualifier, it is also necessary for some member functions to add a const!

if a class is qualified as const, the compiler will determine that all non-const member functions are called illegal!
Because these functions may have the behavior of changing data member values!
Even if it doesn't actually change the idea of data members.

This example is a good illustration:

class Vec{public:    intconst {returnlen;}    intlen(){returnlen;}    intlen;};void use_Vec(const Vec& origin){    int ret1 = origin.len_const();//合法的    intlen();//error:不兼容的类型限定符}

The above problems are often overlooked, fortunately the IDE's own grammar check, so that we can find such errors in time!

11 When you delete an array, do you remember to use delete []?

Matching of--new and delete

This is really simple, but the actual application is not known to forget how many times!

Each new one will match a delete. Unless you confirm that the program will be over soon.
Each new [] should match a delete[]. There is no mistake in following the principle of symmetry!

Finish

"Meditation on C + +": A Checklist for class designers--11 questions about class

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.