4 classes and objects
The three characteristics of C + + object-oriented are encapsulation, inheritance and polymorphism
C + + believes that everything is an object, and the object has its properties and behavior
For example:
People can be objects. Their attributes include name, age, height, weight... And their behaviors include walking, running, jumping, eating, singing
Cars can also be used as objects. Their properties include tires, steering wheel, lights... And their behaviors include carrying people, playing music, playing air conditioning
Objects with the same properties can be abstractly called classes. People belong to humans and cars belong to cars
4.1 packaging
4.1.1 significance of packaging
Encapsulation is one of the three characteristics of C + + object-oriented
Meaning of encapsulation:
- Take attributes and behaviors as a whole to express things in life
- Control attributes and behaviors with permission
Packaging significance I:
When designing classes, attributes and behaviors are written together to represent things
Syntax: class name {access rights: attribute / behavior};
**Example 1: * * design a circle class to calculate the circumference of a circle
Example code:
//PI const double PI = 3.14; //1. Meaning of encapsulation //Attributes and behaviors as a whole are used to express things in life //Encapsulate a circle class to find the circumference of the circle //Class stands for designing a class, followed by the class name class Circle { public: //Access rights public rights //attribute int m_r;//radius //behavior //Gets the circumference of the circle double calculateZC() { //2 * pi * r //Gets the circumference of the circle return 2 * PI * m_r; } }; int main() { //Through the circle class, create the object of the circle // c1 is a concrete circle Circle c1; c1.m_r = 10; //Assign a value to the radius of the circle object //2 * pi * 10 = = 62.8 cout << "The circumference of the circle is: " << c1.calculateZC() << endl; system("pause"); return 0; }
Example 2: design a student class. The attributes include name and student number. You can assign values to the name and student number, and display the student's name and student number
Example 2 Code:
//Student class class Student { public: void setName(string name) { m_name = name; } void setID(int id) { m_id = id; } void showStudent() { cout << "name:" << m_name << " ID:" << m_id << endl; } public: string m_name; int m_id; }; int main() { Student stu; stu.setName("Demacia"); stu.setID(250); stu.showStudent(); system("pause"); return 0; }
Packaging Significance 2:
When designing classes, attributes and behaviors can be controlled under different permissions
There are three types of access rights:
- Public public permission
- Protected protected permissions
- Private private rights
Example:
//Three permissions //Public permission public can be accessed inside the class and outside the class //protected permission can be accessed inside the class, but not outside the class //Private permission private can be accessed inside the class, but not outside the class class Person { //Name public authority public: string m_Name; //Vehicle protection authority protected: string m_Car; //Private permission of bank card password private: int m_Password; public: void func() { m_Name = "Zhang San"; m_Car = "Tractor"; m_Password = 123456; } }; int main() { Person p; p.m_Name = "Li Si"; //p.m_Car = "Benz"// Cannot access outside the protection permission class //p.m_Password = 123; // Cannot access outside private permission class system("pause"); return 0; }
4.1.2 differences between struct and class
In C + +, the only difference between struct and class is that the default access permissions are different
difference:
- The default permission of struct is public
- class default permission is private
class C1 { int m_A; //The default is private permissions }; struct C2 { int m_A; //The default is public permission }; int main() { C1 c1; c1.m_A = 10; //Error, access is private C2 c2; c2.m_A = 10; //Correct, access is public system("pause"); return 0; }
4.1.3 set member property to private
Advantage 1: set all member properties to private, and you can control the read and write permissions yourself
Advantage 2: for write permission, we can detect the validity of data
Example:
class Person { public: //The name setting is readable and writable void setName(string name) { m_Name = name; } string getName() { return m_Name; } //Get age int getAge() { return m_Age; } //Set age void setAge(int age) { if (age < 0 || age > 150) { cout << "You old goblin!" << endl; return; } m_Age = age; } //Lover set to write only void setLover(string lover) { m_Lover = lover; } private: string m_Name; //Readable and writable name int m_Age; //Read only age string m_Lover; //Only lovers }; int main() { Person p; //Name settings p.setName("Zhang San"); cout << "full name: " << p.getName() << endl; //Age setting p.setAge(50); cout << "Age: " << p.getAge() << endl; //Lover settings p.setLover("Cangjing"); //Cout < < lover: "< p.m_ Lover << endl; // Write only attributes, not read system("pause"); return 0; }
Exercise case 1: designing a cube class
Design cube class (Cube)
Find the area and volume of the cube
The global function and member function are used to judge whether the two cubes are equal.
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-cz6wSFxn-1645785708629)(assets/1545533548532.png)]
Exercise case 2: relationship between point and circle
Design a Circle class and a Point class to calculate the relationship between points and circles.
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-fHFFdv3W-1645785708631)(assets/1545533829184.png)]
4.2 initialization and cleaning of objects
- We don't need to delete some basic information in our life when we buy electronic products
- The object-oriented in C + + comes from life. Each object will also have initial settings and settings for cleaning data before object destruction.
4.2.1 constructors and destructors
Object initialization and cleanup are also two very important security issues
An object or variable has no initial state, and the consequences of its use are unknown
Similarly, after using an object or variable, it will also cause some security problems if it is not cleaned up in time
c + + uses constructors and destructors to solve the above problems. These two functions will be automatically called by the compiler to complete object initialization and cleaning.
The initialization and cleanup of objects are what the compiler forces us to do, so if we don't provide construction and destructor, the compiler will provide
The constructor and destructor provided by the compiler are empty implementations.
- Constructor: its main function is to assign value to the member attribute of the object when creating the object. The constructor is automatically called by the compiler without manual call.
- Destructor: the main function is that the system will automatically call and perform some cleaning work before the object is destroyed.
Constructor syntax: class name () {}
- Constructor, no return value and no void
- The function name is the same as the class name
- Constructors can have arguments, so overloading can occur
- When the program calls the object, it will automatically call the construction without manual call, and it will only be called once
Destructor syntax: ~ class name () {}
- Destructor, no return value, no void
- The function name is the same as the class name, and the symbol is added before the name~
- Destructors cannot have parameters, so overloading cannot occur
- The program will automatically call the destructor before the object is destroyed. There is no need to call it manually, and it will only be called once
class Person { public: //Constructor Person() { cout << "Person Constructor call for" << endl; } //Destructor ~Person() { cout << "Person Destructor call for" << endl; } }; void test01() { Person p; } int main() { test01(); system("pause"); return 0; }
4.2.2 classification and calling of constructors
Two classification methods:
According to parameters, it can be divided into parametric structure and nonparametric structure
By type, it can be divided into ordinary structure and copy structure
Three call modes:
Bracket method
Display method
Implicit transformation method
Example:
//1. Constructor classification // According to the classification of parameters, it is divided into parametric and nonparametric constructions. Nonparametric construction is also called default constructor // According to the type, it can be divided into ordinary structure and copy structure class Person { public: //Parameterless (default) constructor Person() { cout << "non-parameter constructor !" << endl; } //Parameterized constructor Person(int a) { age = a; cout << "Parameterized constructor!" << endl; } //copy constructor Person(const Person& p) { age = p.age; cout << "copy constructor !" << endl; } //Destructor ~Person() { cout << "Destructor!" << endl; } public: int age; }; //2. Call of constructor //Call parameterless constructor void test01() { Person p; //Call parameterless constructor } //Call the constructor with parameters void test02() { //2.1 bracket method, commonly used Person p1(10); //Note 1: calling the parameterless constructor cannot be parenthesized. If it is, the compiler considers it a function declaration //Person p2(); //2.2 explicit method Person p2 = Person(10); Person p3 = Person(p2); //Writing Person(10) alone means that the anonymous object is destructed immediately after the end of the current line //2.3 implicit transformation method Person p4 = 10; // Person p4 = Person(10); Person p5 = p4; // Person p5 = Person(p4); //Note 2: anonymous objects cannot be initialized with copy constructors. The compiler considers them object declarations //Person p5(p4); } int main() { test01(); //test02(); system("pause"); return 0; }
4.2.3 call timing of copy constructor
In C + +, there are usually three situations when copying constructor calls
- Initialize a new object with an already created object
- Value is passed to function parameters
- Returns a local object as a value
Example:
class Person { public: Person() { cout << "non-parameter constructor !" << endl; mAge = 0; } Person(int age) { cout << "Parameterized constructor!" << endl; mAge = age; } Person(const Person& p) { cout << "copy constructor !" << endl; mAge = p.mAge; } //Destructor is called before releasing memory. ~Person() { cout << "Destructor!" << endl; } public: int mAge; }; //1. Initialize a new object with an already created object void test01() { Person man(100); //The p object has been created Person newman(man); //Call copy constructor Person newman2 = man; //copy construction //Person newman3; //newman3 = man; // Instead of calling the copy constructor, the assignment operation } //2. The method of value transfer is to transfer values to function parameters //Equivalent to Person p1 = p; void doWork(Person p1) {} void test02() { Person p; //non-parameter constructor doWork(p); } //3. Return local objects as values Person doWork2() { Person p1; cout << (int *)&p1 << endl; return p1; } void test03() { Person p = doWork2(); cout << (int *)&p << endl; } int main() { //test01(); //test02(); test03(); system("pause"); return 0; }
4.2.4 constructor calling rules
By default, the c + + compiler adds at least three functions to a class
1. Default constructor (no parameters, empty function body)
2. Default destructor (no parameters, empty function body)
3. The default copy constructor copies the value of the attribute
Constructor calling rules are as follows:
-
If the user defines a parameterized constructor, c + + will no longer provide a default parameterless construct, but will provide a default copy construct
-
If you define a copy constructor, c + + will not provide another constructor
Example:
class Person { public: //Parameterless (default) constructor Person() { cout << "non-parameter constructor !" << endl; } //Parameterized constructor Person(int a) { age = a; cout << "Parameterized constructor!" << endl; } //copy constructor Person(const Person& p) { age = p.age; cout << "copy constructor !" << endl; } //Destructor ~Person() { cout << "Destructor!" << endl; } public: int age; }; void test01() { Person p1(18); //If you do not write a copy construct, the compiler will automatically add a copy construct and do a shallow copy operation Person p2(p1); cout << "p2 Your age is: " << p2.age << endl; } void test02() { //If the user provides a parameter construct, the compiler will not provide a default construct, but a copy construct Person p1; //At this time, if the user does not provide the default structure, an error will occur Person p2(10); //User provided parameters Person p3(p2); //At this time, if the user does not provide a copy structure, the compiler will provide it //If the user provides a copy construct, the compiler does not provide other constructors Person p4; //At this time, if the user does not provide the default structure, an error will occur Person p5(10); //At this time, if the user does not provide a parameter, an error will occur Person p6(p5); //Users provide their own copy structure } int main() { test01(); system("pause"); return 0; }
4.2.5 deep copy and shallow copy
Deep and shallow copy is not only a classic interview question, but also a common pit
Shallow copy: a simple assignment copy operation
Deep copy: reapply space in the heap area for copy operation
Example:
class Person { public: //Parameterless (default) constructor Person() { cout << "non-parameter constructor !" << endl; } //Parameterized constructor Person(int age ,int height) { cout << "Parameterized constructor!" << endl; m_age = age; m_height = new int(height); } //copy constructor Person(const Person& p) { cout << "copy constructor !" << endl; //If you do not use deep copy to create new memory in the heap, it will lead to the problem of repeatedly freeing the heap caused by shallow copy m_age = p.m_age; m_height = new int(*p.m_height); } //Destructor ~Person() { cout << "Destructor!" << endl; if (m_height != NULL) { delete m_height; } } public: int m_age; int* m_height; }; void test01() { Person p1(18, 180); Person p2(p1); cout << "p1 Age: " << p1.m_age << " Height: " << *p1.m_height << endl; cout << "p2 Age: " << p2.m_age << " Height: " << *p2.m_height << endl; } int main() { test01(); system("pause"); return 0; }
Conclusion: if the attribute is opened in the heap area, you must provide your own copy constructor to prevent the problems caused by shallow copy
4.2.6 initialization list
effect:
C + + provides the syntax to initialize the list of attributes
Syntax: constructor (): Property 1 (value 1), property 2 (value 2) {}
Example:
class Person { public: Traditional initialization //Person(int a, int b, int c) { // m_A = a; // m_B = b; // m_C = c; //} //Initialization list mode Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {} void PrintPerson() { cout << "mA:" << m_A << endl; cout << "mB:" << m_B << endl; cout << "mC:" << m_C << endl; } private: int m_A; int m_B; int m_C; }; int main() { Person p(1, 2, 3); p.PrintPerson(); system("pause"); return 0; }
4.2.7 class objects as class members
A member in a C + + class can be an object of another class, which we call an object member
For example:
class A {} class B { A aï¼› }
Class B has object A as A member and A as an object member
Then, when creating B objects, which is the order of construction and Deconstruction of A and B?
Example:
class Phone { public: Phone(string name) { m_PhoneName = name; cout << "Phone structure" << endl; } ~Phone() { cout << "Phone Deconstruction" << endl; } string m_PhoneName; }; class Person { public: //The initialization list tells the compiler which constructor to call Person(string name, string pName) :m_Name(name), m_Phone(pName) { cout << "Person structure" << endl; } ~Person() { cout << "Person Deconstruction" << endl; } void playGame() { cout << m_Name << " use" << m_Phone.m_PhoneName << " Brand mobile phone! " << endl; } string m_Name; Phone m_Phone; }; void test01() { //When a member in a class is an object of another class, we call it an object member //The order of construction is: call the construction of object members first, and then call the construction of this class //The tectonic sequence is opposite to that of the structure Person p("Zhang San" , "Apple X"); p.playGame(); } int main() { test01(); system("pause"); return 0; }
4.2.8 static members
Static members are called static members by adding the keyword static before member variables and member functions
Static members are divided into:
- Static member variable
- All objects share the same data
- Allocate memory at compile time
- In class declaration, out of class initialization
- Static member function
- All objects share the same function
- Static member functions can only access static member variables
Example 1: static member variables
class Person { public: static int m_A; //Static member variable //Characteristics of static member variables: //1 allocate memory during compilation //2. Declaration within class and initialization outside class //3 all objects share the same data private: static int m_B; //Static member variables also have access rights }; int Person::m_A = 10; int Person::m_B = 10; void test01() { //There are two ways to access static member variables //1. Through object Person p1; p1.m_A = 100; cout << "p1.m_A = " << p1.m_A << endl; Person p2; p2.m_A = 200; cout << "p1.m_A = " << p1.m_A << endl; //Share the same data cout << "p2.m_A = " << p2.m_A << endl; //2. By class name cout << "m_A = " << Person::m_A << endl; //cout << "m_B = " << Person::m_ B << endl; // Private access not available } int main() { test01(); system("pause"); return 0; }
Example 2: static member function
class Person { public: //Static member function features: //1 programs share a function //2 static member functions can only access static member variables static void func() { cout << "func call" << endl; m_A = 100; //m_B = 100; // Error, non static member variables cannot be accessed } static int m_A; //Static member variable int m_B; // private: //Static member functions also have access rights static void func2() { cout << "func2 call" << endl; } }; int Person::m_A = 10; void test01() { //There are two ways to access static member variables //1. Through object Person p1; p1.func(); //2. By class name Person::func(); //Person::func2(); // Private access not available } int main() { test01(); system("pause"); return 0; }
4.3 C + + object model and this pointer
4.3.1 member variables and member functions are stored separately
In C + +, member variables and member functions in a class are stored separately
Only non static member variables belong to objects of a class
class Person { public: Person() { mA = 0; } //Non static member variables occupy object space int mA; //Static member variables do not occupy object space static int mB; //Functions do not occupy object space, and all functions share a function instance void func() { cout << "mA:" << this->mA << endl; } //Static member functions also do not occupy object space static void sfunc() { } }; int main() { cout << sizeof(Person) << endl; system("pause"); return 0; }
4.3.2 this pointer concept
Through 4.3.1, we know that in C + +, member variables and member functions are stored separately
Each non static member function will only produce a function instance, that is, multiple objects of the same type will share a piece of code
So the question is: how does this piece of code distinguish that object from calling itself?
c + + solves the above problems by providing special object pointer and this pointer. This pointer points to the object to which the called member function belongs
this pointer is a pointer implied in every non static member function
this pointer does not need to be defined and can be used directly
Purpose of this pointer:
- When a formal parameter has the same name as a member variable, it can be distinguished by this pointer
- Return the object itself in the non static member function of the class. You can use return *this
class Person { public: Person(int age) { //1. When a formal parameter has the same name as a member variable, it can be distinguished by this pointer this->age = age; } Person& PersonAddPerson(Person p) { this->age += p.age; //Returns the object itself return *this; } int age; }; void test01() { Person p1(10); cout << "p1.age = " << p1.age << endl; Person p2(10); p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); cout << "p2.age = " << p2.age << endl; } int main() { test01(); system("pause"); return 0; }
4.3.3 null pointer accessing member functions
C + + hollow pointer can also call member functions, but pay attention to whether this pointer is used
If this pointer is used, it needs to be judged to ensure the robustness of the code
Example:
//Null pointer access member function class Person { public: void ShowClassName() { cout << "I am Person class!" << endl; } void ShowPerson() { if (this == NULL) { return; } cout << mAge << endl; } public: int mAge; }; void test01() { Person * p = NULL; p->ShowClassName(); //Null pointer, member functions can be called p->ShowPerson(); //However, if this pointer is used in the member function, it is not allowed } int main() { test01(); system("pause"); return 0; }
4.3.4 const modifier member function
Constant function:
- After adding const to the member function, we call it a constant function
- Member properties cannot be modified within a constant function
- After the keyword mutable is added to the member attribute declaration, it can still be modified in the constant function
Constant object:
- Add const before declaring an object to call it a constant object
- Constant objects can only call constant functions
Example:
class Person { public: Person() { m_A = 0; m_B = 0; } //The essence of this pointer is a pointer constant, and the pointer cannot be modified //If you want the value pointed to by the pointer to be immutable, you need to declare a constant function void ShowPerson() const { //const Type* const pointer; //this = NULL; // Cannot modify the pointer pointing to Person* const this; //this->mA = 100; // However, the data of the object pointed to by this pointer can be modified //const modifies the member function, which indicates that the data of the memory space pointed to by the pointer cannot be modified, except for the variable modified by mutable this->m_B = 100; } void MyFunc() const { //mA = 10000; } public: int m_A; mutable int m_B; //Modifiable variable }; //const modifier object void test01() { const Person person; //const object cout << person.m_A << endl; //person.mA = 100; // A constant object cannot modify the value of a member variable, but it can be accessed person.m_B = 100; //However, mutable can be modified to modify member variables for constant objects //Constant object access member function person.MyFunc(); //Constant objects cannot call const functions } int main() { test01(); system("pause"); return 0; }
4.4 friends
In life, your home has a public living room and a private bedroom
All guests in the living room can enter, but your bedroom is private, that is, only you can enter
But you can also allow your best friend and best friend to go in.
In the program, some private properties also want to be accessed by special functions or classes outside the class, so you need to use friend technology
The purpose of a friend is to let a function or class access private members in another class
The keyword of friend is friend
Three implementations of friends
- Global function as friend
- Class as friend
- Member function as friend
4.4.1 global functions as friends
class Building { //Tell the compiler that the goodGay global function is a good friend of the Building class and can access the private content in the class friend void goodGay(Building * building); public: Building() { this->m_SittingRoom = "a living room"; this->m_BedRoom = "bedroom"; } public: string m_SittingRoom; //a living room private: string m_BedRoom; //bedroom }; void goodGay(Building * building) { cout << "Good friends are visiting: " << building->m_SittingRoom << endl; cout << "Good friends are visiting: " << building->m_BedRoom << endl; } void test01() { Building b; goodGay(&b); } int main(){ test01(); system("pause"); return 0; }
4.4.2 making friends
class Building; class goodGay { public: goodGay(); void visit(); private: Building *building; }; class Building { //Tell the compiler that goodGay class is a good friend of Building class and can access the private content of Building class friend class goodGay; public: Building(); public: string m_SittingRoom; //a living room private: string m_BedRoom;//bedroom }; Building::Building() { this->m_SittingRoom = "a living room"; this->m_BedRoom = "bedroom"; } goodGay::goodGay() { building = new Building; } void goodGay::visit() { cout << "Good friends are visiting" << building->m_SittingRoom << endl; cout << "Good friends are visiting" << building->m_BedRoom << endl; } void test01() { goodGay gg; gg.visit(); } int main(){ test01(); system("pause"); return 0; }
4.4.3 member functions as friends
class Building; class goodGay { public: goodGay(); void visit(); //Only use the visit function as a good friend of Building, and you can send and access private content in Building void visit2(); private: Building *building; }; class Building { //Tell the compiler that the visit member function in the goodGay class is a good friend of Building and can access private content friend void goodGay::visit(); public: Building(); public: string m_SittingRoom; //a living room private: string m_BedRoom;//bedroom }; Building::Building() { this->m_SittingRoom = "a living room"; this->m_BedRoom = "bedroom"; } goodGay::goodGay() { building = new Building; } void goodGay::visit() { cout << "Good friends are visiting" << building->m_SittingRoom << endl; cout << "Good friends are visiting" << building->m_BedRoom << endl; } void goodGay::visit2() { cout << "Good friends are visiting" << building->m_SittingRoom << endl; //Cout < < good friends are visiting < < building - > m_ BedRoom << endl; } void test01() { goodGay gg; gg.visit(); } int main(){ test01(); system("pause"); return 0; }
4.5 operator overloading
Operator overloading concept: redefine the existing operators and give them another function to adapt to different data types
4.5.1 overloading of plus operator
Function: realize the operation of adding two user-defined data types
class Person { public: Person() {}; Person(int a, int b) { this->m_A = a; this->m_B = b; } //The member function implements the overloading of the + operator Person operator+(const Person& p) { Person temp; temp.m_A = this->m_A + p.m_A; temp.m_B = this->m_B + p.m_B; return temp; } public: int m_A; int m_B; }; //Global function implementation + operator overload //Person operator+(const Person& p1, const Person& p2) { // Person temp(0, 0); // temp.m_A = p1.m_A + p2.m_A; // temp.m_B = p1.m_B + p2.m_B; // return temp; //} //Operator overloading can cause function overloading Person operator+(const Person& p2, int val) { Person temp; temp.m_A = p2.m_A + val; temp.m_B = p2.m_B + val; return temp; } void test() { Person p1(10, 10); Person p2(20, 20); //Member function mode Person p3 = p2 + p1; //Equivalent to P2 operaor+(p1) cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl; Person p4 = p3 + 10; //Equivalent to operator+(p3,10) cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl; } int main() { test(); system("pause"); return 0; }
Summary 1: the operators of expressions with built-in data types cannot be changed
Summary 2: do not abuse operator overloading
4.5.2 shift left operator overload
Function: you can output custom data types
class Person { friend ostream& operator<<(ostream& out, Person& p); public: Person(int a, int b) { this->m_A = a; this->m_B = b; } //The member function can't realize P < < cout, which is not the effect we want //void operator<<(Person& p){ //} private: int m_A; int m_B; }; //Global function to achieve left shift overload //There can only be one ostream object ostream& operator<<(ostream& out, Person& p) { out << "a:" << p.m_A << " b:" << p.m_B; return out; } void test() { Person p1(10, 20); cout << p1 << "hello world" << endl; //Chain programming } int main() { test(); system("pause"); return 0; }
Conclusion: overloading the shift left operator and cooperating with friends can output user-defined data types
Overloaded increment operator
Function: realize your own integer data by overloading the increment operator
class MyInteger { friend ostream& operator<<(ostream& out, MyInteger myint); public: MyInteger() { m_Num = 0; } //Front++ MyInteger& operator++() { //First++ m_Num++; //Return again return *this; } //Postposition++ MyInteger operator++(int) { //Return first MyInteger temp = *this; //Record the current value of itself, and then add 1 to its own value, but the previous value is returned to return before + +; m_Num++; return temp; } private: int m_Num; }; ostream& operator<<(ostream& out, MyInteger myint) { out << myint.m_Num; return out; } //Pre + + first + + and then return void test01() { MyInteger myInt; cout << ++myInt << endl; cout << myInt << endl; } //Post + + return first and then++ void test02() { MyInteger myInt; cout << myInt++ << endl; cout << myInt << endl; } int main() { test01(); //test02(); system("pause"); return 0; }
Summary: the pre increment returns the reference and the post increment returns the value
4.5.4 overload of assignment operator
The c + + compiler adds at least four functions to a class
- Default constructor (no parameters, empty function body)
- Default destructor (no parameters, empty function body)
- The default copy constructor copies the value of the attribute
- The assignment operator operator =, copies the value of the attribute
If there are attributes in the class pointing to the heap area, the problem of deep and shallow copy will also occur during assignment
Example:
class Person { public: Person(int age) { //Opening up age data to the heap area m_Age = new int(age); } //Overload assignment operator Person& operator=(Person &p) { if (m_Age != NULL) { delete m_Age; m_Age = NULL; } //The code provided by the compiler is a shallow copy //m_Age = p.m_Age; //Provide deep copy to solve the problem of shallow copy m_Age = new int(*p.m_Age); //Return to itself return *this; } ~Person() { if (m_Age != NULL) { delete m_Age; m_Age = NULL; } } //Age pointer int *m_Age; }; void test01() { Person p1(18); Person p2(20); Person p3(30); p3 = p2 = p1; //Assignment operation cout << "p1 Your age is:" << *p1.m_Age << endl; cout << "p2 Your age is:" << *p2.m_Age << endl; cout << "p3 Your age is:" << *p3.m_Age << endl; } int main() { test01(); //int a = 10; //int b = 20; //int c = 30; //c = b = a; //cout << "a = " << a << endl; //cout << "b = " << b << endl; //cout << "c = " << c << endl; system("pause"); return 0; }
4.5.5 overloading of relational operators
Function: overloads the relational operator, allowing two user-defined type objects to be compared
Example:
class Person { public: Person(string name, int age) { this->m_Name = name; this->m_Age = age; }; bool operator==(Person & p) { if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) { return true; } else { return false; } } bool operator!=(Person & p) { if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) { return false; } else { return true; } } string m_Name; int m_Age; }; void test01() { //int a = 0; //int b = 0; Person a("Sun WuKong", 18); Person b("Sun WuKong", 18); if (a == b) { cout << "a and b equal" << endl; } else { cout << "a and b Unequal" << endl; } if (a != b) { cout << "a and b Unequal" << endl; } else { cout << "a and b equal" << endl; } } int main() { test01(); system("pause"); return 0; }
4.5.6 function call operator overloading
- The function call operator () can also be overloaded
- Because the method used after overloading is very similar to the call of function, it is called imitation function
- Imitation function has no fixed writing method and is very flexible
Example:
class MyPrint { public: void operator()(string text) { cout << text << endl; } }; void test01() { //Overloaded () operators are also called functors MyPrint myFunc; myFunc("hello world"); } class MyAdd { public: int operator()(int v1, int v2) { return v1 + v2; } }; void test02() { MyAdd add; int ret = add(10, 10); cout << "ret = " << ret << endl; //Anonymous object call cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl; } int main() { test01(); test02(); system("pause"); return 0; }
4.6 succession
Inheritance is one of the three characteristics of object-oriented
There are special relationships between some classes, such as the following figure:
[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-uMDYpzJ9-1646010240157)(assets/1544861202252.png)]
We found that when defining these classes, the lower level members not only have the commonalities of the upper level, but also have their own characteristics.
At this time, we can consider using inheritance technology to reduce duplicate code
4.6.1 basic syntax of inheritance
For example, we can see that in many websites, there are public headers, public bottoms, and even public lists on the left. Only the central content is different
Next, we use the common writing method and the inheritance writing method to realize the content in the web page, and take a look at the significance and benefits of inheritance
Common implementation:
//Java page class Java { public: void header() { cout << "Home page, open class, login, registration...(Common head)" << endl; } void footer() { cout << "Help center, communication and cooperation, station map...(Common bottom)" << endl; } void left() { cout << "Java,Python,C++...(Public classification list)" << endl; } void content() { cout << "JAVA Subject video" << endl; } }; //Python page class Python { public: void header() { cout << "Home page, open class, login, registration...(Common head)" << endl; } void footer() { cout << "Help center, communication and cooperation, station map...(Common bottom)" << endl; } void left() { cout << "Java,Python,C++...(Public classification list)" << endl; } void content() { cout << "Python Subject video" << endl; } }; //C + + page class CPP { public: void header() { cout << "Home page, open class, login, registration...(Common head)" << endl; } void footer() { cout << "Help center, communication and cooperation, station map...(Common bottom)" << endl; } void left() { cout << "Java,Python,C++...(Public classification list)" << endl; } void content() { cout << "C++Subject video" << endl; } }; void test01() { //Java page cout << "Java The download video page is as follows: " << endl; Java ja; ja.header(); ja.footer(); ja.left(); ja.content(); cout << "--------------------" << endl; //Python page cout << "Python The download video page is as follows: " << endl; Python py; py.header(); py.footer(); py.left(); py.content(); cout << "--------------------" << endl; //C + + page cout << "C++The download video page is as follows: " << endl; CPP cp; cp.header(); cp.footer(); cp.left(); cp.content(); } int main() { test01(); system("pause"); return 0; }
Inheritance implementation:
//Public page class BasePage { public: void header() { cout << "Home page, open class, login, registration...(Common head)" << endl; } void footer() { cout << "Help center, communication and cooperation, station map...(Common bottom)" << endl; } void left() { cout << "Java,Python,C++...(Public classification list)" << endl; } }; //Java page class Java : public BasePage { public: void content() { cout << "JAVA Subject video" << endl; } }; //Python page class Python : public BasePage { public: void content() { cout << "Python Subject video" << endl; } }; //C + + page class CPP : public BasePage { public: void content() { cout << "C++Subject video" << endl; } }; void test01() { //Java page cout << "Java The download video page is as follows: " << endl; Java ja; ja.header(); ja.footer(); ja.left(); ja.content(); cout << "--------------------" << endl; //Python page cout << "Python The download video page is as follows: " << endl; Python py; py.header(); py.footer(); py.left(); py.content(); cout << "--------------------" << endl; //C + + page cout << "C++The download video page is as follows: " << endl; CPP cp; cp.header(); cp.footer(); cp.left(); cp.content(); } int main() { test01(); system("pause"); return 0; }
Summary:
Benefits of inheritance: you can reduce duplicate code
class A : public B;
Class A is called a subclass or derived class
Class B is called a parent or base class
Members in derived classes consist of two parts:
One is inherited from the base class, and the other is added by itself.
Inherited from the base class shows its commonness, while the new members reflect its individuality.
4.6.2 inheritance mode
Inheritance syntax: class subclass: inheritance method parent class
There are three inheritance methods:
- Public succession
- Protect inheritance
- Private inheritance
Example:
class Base1 { public: int m_A; protected: int m_B; private: int m_C; }; //Public succession class Son1 :public Base1 { public: void func() { m_A; //Access to public permissions m_B; //Access protected permissions //m_C; // Inaccessible } }; void myClass() { Son1 s1; s1.m_A; //Other classes can only access public permissions } //Protect inheritance class Base2 { public: int m_A; protected: int m_B; private: int m_C; }; class Son2:protected Base2 { public: void func() { m_A; //Access protected permissions m_B; //Access protected permissions //m_C; // Inaccessible } }; void myClass2() { Son2 s; //s.m_A; // Inaccessible } //Private inheritance class Base3 { public: int m_A; protected: int m_B; private: int m_C; }; class Son3:private Base3 { public: void func() { m_A; //Access to private rights m_B; //Access to private rights //m_C; // Inaccessible } }; class GrandSon3 :public Son3 { public: void func() { //Son3 is a private inheritance, so the properties of inherited son3 cannot be accessed in GrandSon3 //m_A; //m_B; //m_C; } };
4.6.3 object model in inheritance
Question: which members inherited from the parent class belong to the subclass object?
Example:
class Base { public: int m_A; protected: int m_B; private: int m_C; //Private members are just hidden, but they will continue }; //Public succession class Son :public Base { public: int m_D; }; void test01() { cout << "sizeof Son = " << sizeof(Son) << endl; } int main() { test01(); system("pause"); return 0; }
Use tools to view:
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-byISm7aM-1646010240160)(assets/1545881904150.png)]
After opening the tool window, locate the drive letter of the current CPP file
Then enter: cl /d1 reportSingleClassLayout to view the file name of the class name
The effect is as follows:
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Uj2FU5Sd-1646010240160)(assets/1545882158050.png)]
Conclusion: the private members in the parent class are also inherited by the child class, but they can't be accessed after being hidden by the compiler
4.6.4 construction and deconstruction sequence in inheritance
After the subclass inherits from the parent class, when the subclass object is created, the constructor of the parent class will also be called
Question: who comes first and who comes second in the construction and deconstruction order of parent and child classes?
Example:
class Base { public: Base() { cout << "Base Constructor!" << endl; } ~Base() { cout << "Base Destructor!" << endl; } }; class Son : public Base { public: Son() { cout << "Son Constructor!" << endl; } ~Son() { cout << "Son Destructor!" << endl; } }; void test01() { //In inheritance, the constructor of the parent class is called first, and then the constructor of the child class is called. The deconstruction order is opposite to that of the construction Son s; } int main() { test01(); system("pause"); return 0; }
Summary: in inheritance, the constructor of the parent class is called first, and then the constructor of the child class is called. The deconstruction order is opposite to that of the construction
4.6.5 handling method of inheriting members with the same name
Question: when there are members with the same name in the subclass and parent class, how can you access the data with the same name in the subclass or parent class through the subclass object?
- Members with the same name of the subclass can be accessed directly
- Scope is required to access the member with the same name as the parent class
Example:
class Base { public: Base() { m_A = 100; } void func() { cout << "Base - func()call" << endl; } void func(int a) { cout << "Base - func(int a)call" << endl; } public: int m_A; }; class Son : public Base { public: Son() { m_A = 200; } //When a subclass has a member function with the same name as the parent class, the subclass will hide all versions of the member function with the same name in the parent class //If you want to access the member function with the same name hidden in the parent class, you need to add the scope of the parent class void func() { cout << "Son - func()call" << endl; } public: int m_A; }; void test01() { Son s; cout << "Son Lower m_A = " << s.m_A << endl; cout << "Base Lower m_A = " << s.Base::m_A << endl; s.func(); s.Base::func(); s.Base::func(10); } int main() { test01(); system("pause"); return EXIT_SUCCESS; }
Summary:
- Subclass objects can directly access members with the same name in subclasses
- The subclass object plus scope can access the member with the same name as the parent class
- When a subclass has a member function with the same name as the parent class, the subclass will hide the member function with the same name in the parent class, and the scope can access the function with the same name in the parent class
4.6.6 processing method of inheriting static members with the same name
Question: how do static members with the same name in inheritance access on subclass objects?
Static members and non static members have the same name and are handled in the same way
- Members with the same name of the subclass can be accessed directly
- Scope is required to access the member with the same name as the parent class
Example:
class Base { public: static void func() { cout << "Base - static void func()" << endl; } static void func(int a) { cout << "Base - static void func(int a)" << endl; } static int m_A; }; int Base::m_A = 100; class Son : public Base { public: static void func() { cout << "Son - static void func()" << endl; } static int m_A; }; int Son::m_A = 200; //Member property with the same name void test01() { //Access by object cout << "Access via object: " << endl; Son s; cout << "Son lower m_A = " << s.m_A << endl; cout << "Base lower m_A = " << s.Base::m_A << endl; //Access by class name cout << "Access by class name: " << endl; cout << "Son lower m_A = " << Son::m_A << endl; cout << "Base lower m_A = " << Son::Base::m_A << endl; } //Member function with the same name void test02() { //Access by object cout << "Access via object: " << endl; Son s; s.func(); s.Base::func(); cout << "Access by class name: " << endl; Son::func(); Son::Base::func(); //In case of the same name, the subclass will hide all the member functions with the same name in the parent class and need to add scope access Son::Base::func(100); } int main() { //test01(); test02(); system("pause"); return 0; }
Summary: static members with the same name are handled in the same way as non static members, except that there are two access methods (through object and class name)
4.6.7 multi inheritance syntax
C + + allows a class to inherit multiple classes
Syntax: class subclass: inheritance method parent class 1, inheritance method parent class 2
Multiple inheritance may cause members with the same name to appear in the parent class, which needs to be distinguished by scope
Multi inheritance is not recommended in the actual development of C + +
Example:
class Base1 { public: Base1() { m_A = 100; } public: int m_A; }; class Base2 { public: Base2() { m_A = 200; //Start with m_B will not be a problem, but if it is changed to mA, there will be ambiguity } public: int m_A; }; //Syntax: class subclass: inheritance method parent class 1, inheritance method parent class 2 class Son : public Base2, public Base1 { public: Son() { m_C = 300; m_D = 400; } public: int m_C; int m_D; }; //Multi inheritance is easy to produce the situation of members with the same name //By using the class name and scope, you can distinguish which members of the base class are called void test01() { Son s; cout << "sizeof Son = " << sizeof(s) << endl; cout << s.Base1::m_A << endl; cout << s.Base2::m_A << endl; } int main() { test01(); system("pause"); return 0; }
Summary: in multi inheritance, if the parent class has the same name, the scope should be added when the child class is used
4.6.8 diamond inheritance
Diamond inheritance concept:
Two derived classes inherit the same base class
Another class inherits two derived classes at the same time
This inheritance is called diamond inheritance, or diamond inheritance
Diamond inheritance problem:
-
Sheep inherits animal data, and camel also inherits animal data. When grass mud horse uses data, it will produce ambiguity.
-
Grass Mud Horse inherits two copies of data from animals. In fact, we should be clear that we only need one copy of this data.
Example:
class Animal { public: int m_Age; }; //After adding the virtual keyword before inheritance, it becomes virtual inheritance //At this time, the public parent class Animal is called the virtual base class class Sheep : virtual public Animal {}; class Tuo : virtual public Animal {}; class SheepTuo : public Sheep, public Tuo {}; void test01() { SheepTuo st; st.Sheep::m_Age = 100; st.Tuo::m_Age = 200; cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl; cout << "st.m_Age = " << st.m_Age << endl; } int main() { test01(); system("pause"); return 0; }
Summary:
- The main problem caused by diamond inheritance is that subclasses inherit two copies of the same data, resulting in waste of resources and meaninglessness
- Using virtual inheritance can solve the problem of diamond inheritance
4.7 polymorphism
4.7.1 basic concept of polymorphism
Polymorphism is one of the three characteristics of C + + object-oriented
Polymorphisms fall into two categories
- Static polymorphism: function overloading and operator overloading belong to static polymorphism and reuse function names
- Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism
Difference between static polymorphism and dynamic polymorphism:
- Statically polymorphic function address early binding - the function address is determined in the compilation stage
- Dynamic polymorphic function address late binding - determine the function address at the run-time
The following is a case study
class Animal { public: //The Speak function is an imaginary function //The virtual keyword is added in front of the function to become a virtual function, so the compiler cannot determine the function call when compiling. virtual void speak() { cout << "Animals are talking" << endl; } }; class Cat :public Animal { public: void speak() { cout << "The kitten is talking" << endl; } }; class Dog :public Animal { public: void speak() { cout << "The dog is talking" << endl; } }; //If we want to pass in any object, we will call the function of any object //If the function address can be determined at the compilation stage, then static binding //If the function address can only be determined at the running stage, it is dynamic binding void DoSpeak(Animal & animal) { animal.speak(); } // //Polymorphism meets the following conditions: //1. There is an inheritance relationship //2. Subclasses override virtual functions in the parent class //Polymorphic use: //A parent class pointer or reference points to a child class object void test01() { Cat cat; DoSpeak(cat); Dog dog; DoSpeak(dog); } int main() { test01(); system("pause"); return 0; }
Summary:
Polymorphic condition
- There is an inheritance relationship
- Subclasses override virtual functions in the parent class
Polymorphic service conditions
- Pointer to parent or child class
Rewriting: when the return value type of a function is exactly the same as the parameter list of the function name, it is called rewriting
4.7.2 polymorphic case I - calculator
Case description:
Using common writing method and polymorphic technology, a calculator class is designed to realize the operation of two operands
Advantages of polymorphism:
- Clear code organization
- Strong readability
- It is conducive to the expansion and maintenance in the early and later stages
Example:
//Common implementation class Calculator { public: int getResult(string oper) { if (oper == "+") { return m_Num1 + m_Num2; } else if (oper == "-") { return m_Num1 - m_Num2; } else if (oper == "*") { return m_Num1 * m_Num2; } //If you want to provide new operations, you need to modify the source code } public: int m_Num1; int m_Num2; }; void test01() { //General implementation test Calculator c; c.m_Num1 = 10; c.m_Num2 = 10; cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl; cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl; cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl; } //Polymorphic implementation //Abstract calculator class //Advantages of polymorphism: the code organization structure is clear and readable, which is conducive to early and later expansion and maintenance class AbstractCalculator { public : virtual int getResult() { return 0; } int m_Num1; int m_Num2; }; //Addition calculator class AddCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 + m_Num2; } }; //Subtraction calculator class SubCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 - m_Num2; } }; //Multiplication calculator class MulCalculator :public AbstractCalculator { public: int getResult() { return m_Num1 * m_Num2; } }; void test02() { //Create addition calculator AbstractCalculator *abc = new AddCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; //When you run out, remember to destroy it //Create subtraction calculator abc = new SubCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; //Create multiplication calculator abc = new MulCalculator; abc->m_Num1 = 10; abc->m_Num2 = 10; cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; } int main() { //test01(); test02(); system("pause"); return 0; }
Conclusion: C + + development advocates the use of polymorphism to design program architecture, because polymorphism has many advantages
4.7.3 pure virtual functions and abstract classes
In polymorphism, the implementation of virtual functions in the parent class is meaningless, which is mainly the content rewritten by calling subclasses
Therefore, virtual functions can be changed to pure virtual functions
Pure virtual function syntax: virtual return value type function name (parameter list) = 0;
When there are pure virtual functions in a class, this class is also called an abstract class
Abstract class features:
- Cannot instantiate object
- Subclasses must override pure virtual functions in abstract classes, otherwise they also belong to abstract classes
Example:
class Base { public: //Pure virtual function //As long as there is a pure virtual function in a class, it is called an abstract class //Abstract classes cannot instantiate objects //Subclasses must override pure virtual functions in the parent class, otherwise they also belong to abstract classes virtual void func() = 0; }; class Son :public Base { public: virtual void func() { cout << "func call" << endl; }; }; void test01() { Base * base = NULL; //base = new Base; // Error, abstract class cannot instantiate object base = new Son; base->func(); delete base;//Remember to destroy } int main() { test01(); system("pause"); return 0; }
4.7.4 case 2 - making drinks
Case description:
The general process of making drinks is: boiling water - brewing - pouring into the cup - adding accessories
This case is realized by using polymorphic technology, providing Abstract production of beverage base class and subclass production of coffee and tea
Example:
//Abstract making drinks class AbstractDrinking { public: //Boil water virtual void Boil() = 0; //Brew virtual void Brew() = 0; //Pour into a cup virtual void PourInCup() = 0; //Add excipients virtual void PutSomething() = 0; //Specified process void MakeDrink() { Boil(); Brew(); PourInCup(); PutSomething(); } }; //Making coffee class Coffee : public AbstractDrinking { public: //Boil water virtual void Boil() { cout << "Boiled farmer spring!" << endl; } //Brew virtual void Brew() { cout << "Brew coffee!" << endl; } //Pour into a cup virtual void PourInCup() { cout << "Pour the coffee into the cup!" << endl; } //Add excipients virtual void PutSomething() { cout << "Add milk!" << endl; } }; //Making tea class Tea : public AbstractDrinking { public: //Boil water virtual void Boil() { cout << "Boiled tap water!" << endl; } //Brew virtual void Brew() { cout << "Brewing tea!" << endl; } //Pour into a cup virtual void PourInCup() { cout << "Pour the tea into the cup!" << endl; } //Add excipients virtual void PutSomething() { cout << "Add medlar!" << endl; } }; //Business function void DoWork(AbstractDrinking* drink) { drink->MakeDrink(); delete drink; } void test01() { DoWork(new Coffee); cout << "--------------" << endl; DoWork(new Tea); } int main() { test01(); system("pause"); return 0; }
4.7.5 virtual deconstruction and pure virtual deconstruction
When polymorphism is used, if an attribute in a subclass is opened to the heap, the parent class pointer cannot call the destructor code of the subclass when it is released
Solution: change the destructor in the parent class to virtual destructor or pure virtual destructor
The commonness between virtual destruct and pure virtual destruct:
- You can solve the problem of releasing subclass objects from parent class pointers
- All need specific function implementation
Difference between virtual destruct and pure virtual destruct:
- If it is a pure virtual destructor, this class belongs to an abstract class and cannot instantiate an object
Virtual destructor syntax:
virtual ~ class name () {}
Pure virtual destructor syntax:
virtual ~ class name () = 0;
Class name:: ~ class name () {}
Example:
class Animal { public: Animal() { cout << "Animal Constructor call!" << endl; } virtual void Speak() = 0; //The destructor plus the virtual keyword becomes a virtual destructor //virtual ~Animal() //{ // Cout < < "animal virtual destructor call!"<< endl; //} virtual ~Animal() = 0; }; Animal::~Animal() { cout << "Animal Pure virtual destructor call!" << endl; } //Like a class containing ordinary pure virtual functions, a class containing pure virtual destructors is also an abstract class. Cannot be instantiated. class Cat : public Animal { public: Cat(string name) { cout << "Cat Constructor call!" << endl; m_Name = new string(name); } virtual void Speak() { cout << *m_Name << "The kitten is talking!" << endl; } ~Cat() { cout << "Cat Destructor call!" << endl; if (this->m_Name != NULL) { delete m_Name; m_Name = NULL; } } public: string *m_Name; }; void test01() { Animal *animal = new Cat("Tom"); animal->Speak(); //Releasing through the parent class pointer may cause the subclass objects to be unclearly cleaned, resulting in memory leakage //How? Add a virtual destructor to the base class //The virtual destructor is used to release subclass objects through the parent class pointer delete animal; } int main() { test01(); system("pause"); return 0; }
Summary:
​ 1. Virtual destruct or pure virtual destruct is used to solve the problem of releasing subclass objects through the parent class pointer
​ 2. If there is no heap data in the subclass, it can not be written as virtual destruct or pure virtual destruct
​ 3. Classes with pure virtual destructors are also abstract classes
4.7.6 case 3 - computer assembly
Case description:
The main components of the computer are CPU (for calculation), graphics card (for display) and memory module (for storage)
Encapsulate each part into an abstract base class, and provide different manufacturers to produce different parts, such as Intel manufacturers and Lenovo manufacturers
Create a computer class, provide functions that let the computer work, and call the interface of each part
During the test, three different computers were assembled to work
Example:
#include<iostream> using namespace std; //Abstract CPU class class CPU { public: //Abstract computing function virtual void calculate() = 0; }; //Abstract graphics card class class VideoCard { public: //Abstract display function virtual void display() = 0; }; //Abstract memory module class class Memory { public: //Abstract storage function virtual void storage() = 0; }; //Computer class Computer { public: Computer(CPU * cpu, VideoCard * vc, Memory * mem) { m_cpu = cpu; m_vc = vc; m_mem = mem; } //Functions that provide work void work() { //Make the part work and call the interface m_cpu->calculate(); m_vc->display(); m_mem->storage(); } //Provide destructor to release 3 computer parts ~Computer() { //Release CPU parts if (m_cpu != NULL) { delete m_cpu; m_cpu = NULL; } //Release graphics card parts if (m_vc != NULL) { delete m_vc; m_vc = NULL; } //Release memory module parts if (m_mem != NULL) { delete m_mem; m_mem = NULL; } } private: CPU * m_cpu; //Part pointer of CPU VideoCard * m_vc; //Graphics card part pointer Memory * m_mem; //Memory module part pointer }; //Specific manufacturer //Intel manufacturer class IntelCPU :public CPU { public: virtual void calculate() { cout << "Intel of CPU Start counting!" << endl; } }; class IntelVideoCard :public VideoCard { public: virtual void display() { cout << "Intel Your graphics card is starting to show!" << endl; } }; class IntelMemory :public Memory { public: virtual void storage() { cout << "Intel My memory module is beginning to store!" << endl; } }; //Lenovo manufacturer class LenovoCPU :public CPU { public: virtual void calculate() { cout << "Lenovo of CPU Start counting!" << endl; } }; class LenovoVideoCard :public VideoCard { public: virtual void display() { cout << "Lenovo Your graphics card is starting to show!" << endl; } }; class LenovoMemory :public Memory { public: virtual void storage() { cout << "Lenovo My memory module is beginning to store!" << endl; } }; void test01() { //First computer parts CPU * intelCpu = new IntelCPU; VideoCard * intelCard = new IntelVideoCard; Memory * intelMem = new IntelMemory; cout << "The first computer starts working:" << endl; //Create first computer Computer * computer1 = new Computer(intelCpu, intelCard, intelMem); computer1->work(); delete computer1; cout << "-----------------------" << endl; cout << "The second computer starts working:" << endl; //Second computer assembly Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);; computer2->work(); delete computer2; cout << "-----------------------" << endl; cout << "The third computer starts working:" << endl; //Third computer assembly Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);; computer3->work(); delete computer3; }