C ++ basic skills and design pattern series type cast, interface inheritance vs implementation inheritance

Source: Internet
Author: User
Many people who have learned C must remember the type conversion in C, for example:

Float floatnum = 1.234;
Int intnum = (INT) floatnum;
// Intnum = 1

This is a normal type conversion. A slightly risky conversion is as follows:

Float floatnum = 1.234;
Float * pfloatpointer = & floatnum;
Int * pintpointer = (int *) pfloatpointer;
// * Pintpointer is a mess.

C type conversion is very convenient, but it brings more problems. For example, the type conversion of C allows you to convert any type. A very random use of type conversion can easily lead to confusion in the program logic, so that people do not understand the code you write, or the compiler cannot correctly identify your conversion intention, so make the wrong conversion method. Second, the C type conversion is hard to be found. Especially in large projects, you may need to check thousands of lines of code containing "(INT)" If you want to find a problem that has occurred because (uint) is converted to int. Four types of conversions are introduced in C ++ to avoid such situations. Remember that C type conversion is only designed for C and is not suitable for C ++. C ++ supports C type conversion only for backward compatibility considerations.

Let's take a look at four types of conversions in C ++ (for a detailed description of these four types of conversions, see <C ++ programming language> 3rd edition ):

Static_cast <> ()
Dynamic_cast <> ()
Const_cast <> ()
Reinterpret_cast <> ()

One by one
========================== Static_cast =======================
Static_cast <> ()
Static_cast can be used to convert the values of related types, such as converting double to float, float to int, and between associated pointer, and between associated class pointer.

================================

Float floatnum = 1.234;
Int intnum = static_cast <int> (floatnum); // intnum = 1;

============Or ============

Class baseclass {
Public:
Baseclass ();
Virtual ~ Baseclass ();
};

Class derivedclass: Public baseclass {
Public:
Derivedclass ();
~ Derivedclass ();
Void dosomething (void );
};

Baseclass * pbaseclass = new baseclass ();
// No problem, but call pderviedcalss-> dosomething () may crash
Derivedclass * pderivedclass = static_cast <derivedclass *> pbaseclass;

Derivedclass * pderivedclass = new derivedclass ();
// No problem, very safe
Baseclass * pbaseclass = static_cast <baseclass *> (pderivedclass );

It is worth noting that static_cast checks whether the type conversion meets the requirements during program compilation and does not check during the program running. Because there is no runtime overhead, it has little impact on the speed. So you can use static_cast whenever possible when you are very confident about type conversion. In addition, static_cast cannot guarantee that the pointer address before the conversion is the same as the pointer address after the conversion. Especially in the Multi-inheritance class structure, the pointer address often changes.

Below are some of the most dangerous type conversions,

==============================
Unsigned to sign, for example, converting uint to int
Double to float
Long to int (64-bit OS dangerous, 32-bit none)

These conversions are converted from a large value to a small value, or from unsigned to signed. For example:

Unsigned int K = 4294967290;
Int M = static_cast <int> (k );

Here K has exceeded the int value range, so the final result of M is-6. Therefore, be especially careful when performing the preceding type conversion.

========================== Dynamic_cast =======================
Dynamic_cast is used to convert the type between related class pointers. Because dynamic_cast performs a security check on the conversion during running, the running speed of the program is greatly affected, and the/GR of runtime type info must be enabled when the conversion is cheap, therefore, it is not recommended.

To use dynamic_cast, the converted class must contain at least one virtual function, and only the class pointer can be converted. the pointer does not include void *. For example:

Class baseclass {
Public:
Baseclass ();
Virtual ~ Baseclass ();
Virtual void dosomething (void );
};

Class derivedclass: Public baseclass {
Public:
Derivedclass ();
~ Derivedclass ();
Void dosomething (void );
};

Derivedclass * pderivedclass = new derivedclass ();
// No problem
Baseclass * pbaseclass = dynamic_cast <derivedclass *> (pderivedclass );

Baseclass * pbaseclass = new basecalss ();
// If the base class is converted to a derived class, dynamic_cast returns NULL, so pderivedclass = NULL.
Derivedclass * pderivedcalss = dynamic_cast <derivedclass *> (pbaseclass );

========================== Const_cast =============================
As the name suggests, it is to convert the const variable to the non-const variable or volatile to the non-volatile variable. This is the least recommended type of conversion, which can be used only in special cases. If you need to use const_cast in large quantities, it only indicates that the program has design defects.

The usage of const_cast is as follows:
Float floatnum = 12;
Const float * pconstfloatpointer = & floatnum;
// OK. No problem.
Float * pfloatpointer = const_cast <float *> (pconstfloatpointer );
// Compilation error. const_cast can only convert const and non-Const.
Int * pfloatpointer = const_cast <int *> (pconstfloatpointer );

======================== Reinterpret_cast =======================
Reinterpret_cast is the most dangerous conversion type. It does not perform any actual conversion operations on data, instead, data is directly used as another type (similar to the memory copy function ). For example:

Class classx {...};
Class classy {...};

Classx * pclassx = new classx ();
// No problem. However, unless both classx and classy have the same structure, it is not safe and the program can easily crash
Classy * pclassy = reinterpret_cast <classy *> (pclassx );

Reinterpet_cast is useful for converting function pointers. For example, you can store a pointer in a function pointer array:

Typedef void (* funcptr )();
Funcptr funcptrarray [10];
Int dosomething (){...};
// Reinterpret_cast is used here
Funcptrarray [0] = reinterpret_cast <funcptr> (& dosomething );

In general, try to use these four conversions to clearly express your purpose of conversion, and try not to use the C-style conversion.

========================= Design Pattern ============================
Today's design pattern introduces two other concepts: interface inheritance and implementation inheritance.

The last time aear introduced inheritance and delegation, and said that if delegation can be used, it is best not to use inheritance. But what if you must use inheritance? Therefore, there are different ways to use inheritance (inheritance. The first is interface inheritance.

Simply put, interface inheritance defines an abstract class (abstract class), defines all provided class methods in this abstract class as pure virtual functions, and then implements these virtual functions in the derived class. For this abstract class, we can call it interface. (Is it a bit like Com ?). For example, in a game, all enemies can be moved and attacked, but different types of enemies, attacks, and moving methods are different. We can use an abstract class to represent all enemies and implement different methods of movement and attack in the derived class.
Let's take a look at the Code:

Class enemy {
Public:
Virtual void move (void) = 0;
Virtual void attack (void) = 0;
};

// Human enemy
Class humanenemy: Public Enemy {
Public:
Void move (void) {...}; // walk with 2 legs
Void attack (void) {...}; // attack with a fist or weapon
};

// Monster enemy
Class monsterenemy: Public Enemy {
Public:
Void move (void) {...}; // walk with a 4-hop leg
Void attack (void) {...}; // attack with teeth and claws
};

When running AI, we don't need to determine whether the other party is a human or an enemy. We just need to call move and attack directly. The Code is as follows:

Void calculateaction (enemy * penemy)
{
Penemy-> move ();
Penemy-> attack ();
}

Main ()
{
....
Humanenemy * phumanenemy = new humanenemy ();
Monsterenemy * pmonsterenemy = new monsterenemy ();
Calculateaction (static_cast <enemy *> phumanenemy );
Calculateaction (static_cast <enemy *> pmonsterenemy );
....
}

============= Small split line ==============

The implementation inheritance is an example of the cbitmap and ctexture mentioned by aear in the previous chapter. Cbitmap is only a base class and provides the getbitmapheight () method. Ctexture is a derived class of cbitmap. It provides the gettextureheight () method. This structure can easily lead to confusion in the logic structure of the program and is not recommended. Aear's personal opinion is:
Use Delegation
Interface inheritance cannot be used for delegation.
You can't use interface inheritance to redesign your class structure so that you can use the first two methods.
If you re-design the system or use interface inheritance, the overhead is too large, and then use implementation inheritance.

In fact, all implementation inheritance can be implemented through interface inheritance. Specifically, it is to define an abstract class base class. On this basis, it defines the abstract class (the derived class is also interface) of the derived class ), so defined until all the classes to be implemented have their own interface 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.