C ++ basic skills and design pattern Series base class design

Source: Internet
Author: User
Tags class manager

In Oo, the base class has two methods at most. The first method is interface, that is, the base class is abstract class. The most typical example is factory design pattern. the second approach is to provide a mechanism for base class, and then provide several virtual functions, allowing users to overload this class to achieve the purpose of "Customization. A typical example is MFC. It inherits the onpaint and other functions of the CWIN class that provide the message passing mechanism to implement more and more powerful functions.

This time we will focus on the two types of design.

================== Abstract interface ======================

For interface class, the base class must be an abstract class. Its meaning is the abstract concept of a class of objects. For example, the following example:

Class gun {
Public:
Void shoot (void) = 0;
...
};

Gun is an abstract concept of everyone. The specific implementation can be Eagle, AK47, MD5, and so on. All guns have the shooting function, but they have different shooting methods. Therefore, as long as a shoot interface is provided, the content is implemented by the derived class.

When designing an interface class, you need to pay attention to the following:

1. It is best to only provide pure virtual function. In this way, the interface class can be simpler, more pure, more beautiful, and easier to maintain. An interface can only be an interface and should not provide anything else. Because the interface is just a description.

2. It is best not to use data member. Data member often limits the flexibility of the interface class. And there is data member, generally there must be an accessor function, so that the interface class is no longer pure ......

3. If you have to use data member, you must provide a constructor with parameters and set the saved constructor to private. For example:

Class interfaceclass {
PRIVATE:
Uint32 _ datamember;
Public:
Base (){};
... // Other Interface
};

Class derived: Public interfaceclass {
Public:
Derived (){};
};

The derived constructor in the above Code is legal, but because the code writer is careless and forgets to attach an initial value to _ datamember, it is easy to cause program crash. Therefore, the correct method for base class with data member is:

Class interfaceclass {
PRIVATE:
Uint32 _ datamember;
Public:
Base (uint32 data): _ datamember (data ){};
... // Other Interface
};

4. in most cases, the destructor of the base class is usually put in the public and must be implemented, that is, at least the implementation of null, "{}", even if it is a pure virtual destructor, this {} is also indispensable. In many cases, you need to call destructor through the interface to release the object. For example:

Class interfaceclass {
Public:
~ Interfaceclass (){};
};

======= Management of derived class ========
For interface class, if there are too many derived classes, it is difficult to manage them. However, we can achieve this through a unified classmanager. Below is a small example:

// Interface
Class interfaceclass {
Public:
Virtual void someinterface (void) = 0;

PRIVATE:
Friend class classmanager;
~ Interfaceclass () = 0 {};
};

// First concrete derived class
Class derived1: Public interfaceclass {
Public:
Virtual void someinterface (void ){...};

PRIVATE:
Friend class classmanager;
Derived1 (){...};
~ Derived1 (){...};
};

// Second concrete derived class
Class derived2: Public interfaceclass {
Public:
Virtual void someinterface (void ){...};

PRIVATE:
Friend class classmanager;
Derived2 (){...};
~ Derived2 (){...};
};

// Class Manager
// Singleton
Class classmanager
{
Public:
Static classmanager * getinstance (void)
{
Static classmanager instance;
Return & instance;
}

Interfaceclass * getderived1 (void) {return New derived1 ()};
Interfaceclass * getderived2 (void) {return New derived2 ()};

PRIVATE:
Classmanager ();
~ Classmanager ();
};

=============== Concrete base class ==========================

Concrete Base class is usually used. The differences between frequently used objects in the game are very small. These objects can be abstracted through concrete base class to improve the efficiency of code reuse. Below is a small example:

Class stringarray {
Public:
Void addstring (){...};
Void searchstring (){....};
Void sortstring (void) {strategysortstring ();};
...
PRIVATE:
Virtual strategysortstring (void)
{...}; // Bubble sort
};

Class stringarray2: Public stringarray {
...
PRIVATE:
Virtual strategysortstring (void)
{...}; // Quick Sort
};

Suppose we have stringarray, which provides all the basic String Array Operations. However, for different array input methods and deletion methods, the corresponding sorting methods are not very efficient. Therefore, we can set sort to virtual, and then select the correct derived class for different stringarray usage methods.

Note the following points for concrete base class:

1. destructor. I believe this is not to be said, it must be virtual, and ensure that the resources are released clean.

2. Use non-virtual interface. This is the conclusion of the C ++ masters. The reason is that when derived class reloads virtual, the normal operation of the mechanism provided in the base class is often affected. If you use non-public virtual, you can make the program more flexible and check the pre/post condition of the process. For example:

Class stringarray {
Public:
Void sortarray ()
{
// Pre-condition check
Strategysoryarray ();
// Post-condition check
};
PRIVATE:
Virtual void strategysoryarray (void ){...};
};

In this way, the pre/post condition check ensures that the base class users do not make mistakes to the maximum extent. This avoids program bugs.

3. Try to use private virtual. Protected Virtual. It should be placed in protected only when the inherited class needs to call the implementation of the base class. The most famous is the clone function:

Class base {
Protected:
Base * clone (void ){};
};

Class derived: Base {
Protected:
Base * clone (void) {base: Clone ();...};
};

There are other aspects, which are similar to the basic class design. But another important aspect is Operator and copy constructor.

================= Operator ====================

In the base class, operator = is the most important of all operator. For abstract class, it is best to disable operator =. Why? Let's take a look at the Code:

Class base {
Public:
Virtual void interface (void) = 0;
...
};

Class derived1: public base {
PRIVATE:
_ X;
};

Class derived2: public base {
PRIVATE:
_ X;
_ Y;
};

Base * pd1 = new derived1 ();
Base * Pd2 = new derived2 ();

* Pd1 = * Pd2;

Maybe * pd1 = * Pd2 is not common, but it is indeed a legitimate code, which can be checked by the compiler. However, * pd1 = * Pd2 calls Base operator =. When base does not provide operator =, it uses the default bitcopy operation. This default copy operation only copies the memory of the base class, ignoring the memory of the derived class. That is, _ x _ y is not copied.

If we provide a virtual operator = in the base class, the problem can be solved to a certain extent, but the overload operator must undergo type conversion, for example:

Base & derived1: Operator = (base & X ){
....
Static_cast <derived1 *> (X)
....
};

However, the following code can still be compiled to produce incorrect results:

* Pd1 = * Pd2;

Therefore, for abstract class, the most convenient thing is to disable operator = and set it to private.

For concrete base class, either provide operator = or put it in private, but call the operator = of base class in derived class. For example:

Class base {
...
Public:
Base & operator = (base &);
};

Class derived: public base {
...
Public:
Derived & operator = (derived & X)
{
Base: Operator = (X );
...
// Own assignment operations
}
};

This method does not support cast up, that is:
Base * pbase1 = new derived ();
Base * pbase2 = new derived ();
* Pbase1 = * pbase2;

The better way is to put the base operator = in protected, but it does not solve the fundamental problem. The best solution is to use the virtual clone and construct mentioned above. For example:

Class base {
Virtual base * construct (void) {return new base ();};
Virtual base * clone (void) {return new base (* This );};
};

Class derived: public base {
Virtual base * construct (void)
{Return New derived ();};

Virtual base * clone (void)
{Return New derived (* This );};
};

Disable base opeartor =. However, this efficiency will be reduced. In short, the best way is to use abstract interface and disable base from using private operator =.

 

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.