"C ++ meditation" Reading Notes-Agent Class

Source: Internet
Author: User

Andrew Koenig and Barbara Moo are called the "first fairy friends" in the field of c ++ research. They read their books very well organized. This time I want to explain another common problem in C ++.Find a beautiful way to control memory allocation to bind different subclass objects to the container.What a complicated sentence, don't panic, in fact, very simple, follow the pace to see.

First of all, let's assume that we want to design a series of categories of transportation tools. Generally, we will define a base class of transportation tools, which stores all the Members and attributes of all transportation tools, such:

class Vehicle {public:    virtual double weight() const = 0;    virtual void start() = 0;    // ......};


Then there will be some transport inheritance relationships, such:

class RoadVehicle : public Vehicle { /* ...... */ };class AutoVehicle : public RoadVehicle { /* ...... */ };class Aircraft    : public Vehicle { /* ...... */ };class Helicopter  : public Aircraft { /* ...... */ };


Now we need to define a container to save different types of transport.

This requirement seems simple, but not as easy as you think. For example, if we use an array to store different transportation tools, we may first write:

Vehicle parking_lot[1000];


I think it seems wrong to write this. Why?Because Vehicle contains pure virtual functions, Vehicle is an abstract class, and the abstract class does not have objects.So this definition is definitely not feasible. The general analysis ends here, but I will continue to think about it. If I remove all pure virtual functions in Vehicle, this definition seems to be OK and there will be no Syntax problems, but there is another problem, such as the following assignment:

Helicopter x = /* ...... */parking_lot[num_vehicles++] = x;


Such a value will cause the Helicopter object to be converted into a Vehicle object, which will lose its Helicopter attribute, which is not what we want, this is like converting a double number into an integer array and losing its decimal part.

If you see this, someone will propose it right away. Isn't it enough to store the Vehicle pointer in parking_lot? Let's take a look:

Vehicle * parking_lot [1000]; // pointer Array


Repeat the above assignment operation:

Helicopter x = /* ...... */parking_lot[num_vehicles++] = &x;


Everything looks okay, but experienced programmers, such as me, :) can tell at a glance that it is dangerous. Why is it dangerous? Because storing pointers is a dangerous thing. Specifically, here x looks like a local variable,If x is released, the pointer in the parking_lot array immediately becomes a suspension pointer, and you do not know what to point. A responsible programmer cannot do this.

Are we not doing anything about it? No,Since the pointer cannot be put, I can copy this object., As follows:

Helicopter x = /* ...... */parking_lot[num_vehicles++] = new Helicopter(x);


Although it is a waste of time and memory, it seems that we can do this. We need to release the memory allocated by ourselves, so we continue to stipulate that when we delete this parking_lot, we also release the object to which it points. If this is the only way to manage the memory, I think I can accept it, but there is a less obvious problem here. It is the object we put into parking_lot. It must be an object of a known type. When it comes to this, some readers will immediately understand what I mean, that isFor objects with unknown types during compilation, they cannot be saved here.For example, I need to put the parking_lot [q] object in parking_lot [p ].What should I do?We do not know the object type of parking_lot [q], so we cannot copy this object. At the same time, we cannot make two pointers in parking_lot point to the same object, because when we delete this container, the objects in it will also be deleted. If there are two pointers pointing to the same object, it will be deleted twice.. Of course, you can use other methods to avoid it, but it still makes me intolerable.

For unknown objects during compilation, smart programmers have come up with solutions. Why do we need to know what they are? As long as they know what they are and tell us they are OK! Good boy! To be specificWe can let classes inherited from Vehicle to tell others what they are. A simple method is to define the copy pure virtual function in Vehicle.Then, the classes inherited from Vehicle all design their own copy functions to copy and return them to the caller, so that the caller does not need to know what these messy transportation means are. Let's continue to modify the code:

class Vehicle {public:    virtual double weight() const = 0;    virtual void start() = 0;    virtual Vehicle *copy() const = 0;    // ......};


Then we modify the Helicopter class and add a copy function:

Vehicle *Helicopter::copy() const{    return new Helicopter(*this);}


In this way, we no longer need to know the x type or the parking_lot [q] type, and directly call x. copy () function or parking_lot [q]-> copy () function is OK.

parking_lot[num_vehicles++] = x.copy();parking_lot[p] = parking_lot[q]->copy();


We perfectly solved the second problem mentioned above, but programmers have always been pursuing perfection. Is there a way for us to solve this problem of display Processing Memory Allocation? This is where programmers are happy. It is extremely difficult to pursue perfection in other fields, but code can always make us happy. A profound concept is mentioned in "C ++ meditation --"Using classes to represent concepts"What exactly does it mean? That is to say, our design class can not only be a specific thing, but also a concept. For example, you can use classes to represent people, men, and women, similarly, you can use classes to represent the family. People are specific, while family is just a concept. There are definitely people in the family, so you have taken control of the concept of family, in this case, the Controller should not tell me that some people do not have a family. Just give an example !).

Specifically, in code, we define a proxy class to express these different transportation tools,This proxy class should be able to represent different transportation tools. At the same time, it needs to help me manage the memory and be instantiated, because in this way, I don't have to worry about the problem that the Vehicle above is that the abstract class cannot define the container.Therefore, the role of this proxy class is to allow me to define the proxy class container, without the need for me to consider memory management issues, and to support situations where the type is unknown during compilation.

Agency is just a manager who manages transportation tools. It is not a specific thing, just like a celebrity broker. It seems that it must save a star, that is, it must have a pointer to a vehicle, and at the same time it needs to be on the table, so it needs a real constructor, and at the same time it needs to be able to put into the container, therefore, it requires a default constructor:

class VechicleProxy {public:    VechicleProxy();    VechicleProxy(const Vehicle &);    ~VechicleProxy();    VechicleProxy(const VechicleProxy &);    VechicleProxy &operator=(const VechicleProxy &);private:    Vehicle *p;};


I have added several constructors and value assignment operators above, which is not hard to understand. After all, it is a real class. The const Vehicle & parameter copy constructor provides the proxy capability for any traffic means. Everything looks OK,What can we assign to the p pointer in the default constructor? It seems that only 0 can be assigned. This zero pointer is usually a null proxy.. Let's complete the member functions of this proxy class:

VechicleProxy::VechicleProxy(): p(0) { }VechicleProxy::VechicleProxy(const Vehicle &BigStar): p(BigStar.copy()) {}VechicleProxy::~VechicleProxy() { delete p; }VechicleProxy::VechicleProxy(const VechicleProxy &v): p(v.p ? v.p->copy() : 0) {}VechicleProxy::operator=(const VechicleProxy &v){    if (this != &v)    {        delete p;        p = (v.p ? v.p->copy() : 0);    }    return *this;}


There is no extra secret here. Click OK carefully. Here we can finally define a perfect parking_lot.

VehicleProxy parking_lot[1000];Helicopter x;parking_lot[num_vehicles++] = x;


Summary:

When using inheritance and containers, we usually need to solve two problems:Memory AllocationAndBinding of unknown types during compilation.Using a thing that becomes a proxy class, we compress the complex inheritance layers together.So that this class can represent all child types, and the weapon that uses classes to represent concepts is really sharp.


This article from the "cainiao surfaced" blog, please be sure to keep this source http://rangercyh.blog.51cto.com/1444712/1291958

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.