C + + basic learning - expression and processing of complex information (2) - object oriented
The previous article mainly summarized the concept of class introduced by C + + in expressing the characteristics of information structure from single to diverse:
Variables can be used to represent the single characteristics of a thing; Multiple variables can be used to represent multiple features of a thing; A one-dimensional array can be used to represent a feature of multiple things; Represent multiple things with multiple characteristics? A variable can describe a single feature of a thing; An array can describe the same feature of multiple things or multiple similar features of a thing; Structure can describe multiple features of a thing. From variable to structure, it is the change from feature description to thing description. In addition to features, things also have state changes and functions. For example, an image can be scaled and tilted, the brightness can be variable, dimmed, and the color can be changed. To fully describe the characteristics and functions of things, C++The function provided is "class". C++Class is used to describe a class of things with the same characteristics and functions.
class
1. The difference between struct and class
From the perspective of C language, the so-called class is a structure that can call its own members. In C + +, although the keyword struct is still retained, it is no longer a structure in C language, but represents the class shared by the default members.
That is, in C + +, there is no difference between struct C{/code /} and class C{public: / * * /}. For example, the functions implemented by the following two groups of codes are exactly the same.
//Default member public struct Number{ private; float val; public: float pubVal; Number(float inVal); };
//The default member is private class Number{ float val;//External cannot be accessed directly public: float pubVal; Number(float inVal); };
Private members are members that cannot be accessed by external functions
void printPublic(Number num){ cout<<num.pubVal<<endl; } void printPrivate(Number num){ cout<<num.val<<endl; //Error, unable to access private type }
However, from the perspective of C language, the class also retains some struct style, as evidenced by its initialization method and pointer call.
int main(){ Number num{3.14}; //Equivalent to reference constructor printNumber(num); Number* pNum = # //Pointer to num //->Represents the member to which the class pointer points cout<<pNum->pubVal<<endl; system("pause"); return 0; }
Output as
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 3.14
2. explicit structure
Because C + + has very good support for generics, the strength of the language itself may cause users to be lax in the use process, and then increase the maintenance cost. For example, for the following constructor
Number::Number(float inVal){ val = inVal; }
Then the following statements can output the correct value
int main(){ Number num{3.14}; printNumber(num); num = 1.414; printNumber(num); printNumber(0.618); system("pause"); return 0; }
The result is
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 1.414 0.618
Please press any key to continue
It can be seen that there are no errors in these three statements
Number num{3.14};
num = 1.414;
printNumber(0.618);
The first is no problem, it is a simple assignment statement;
Second and third calls the constructor in secret, converting the variables of the floating point type to the Number type. This kind of code with unknown meaning naturally causes maintenance difficulties.
explicit is born to solve this problem.
This implicit conversion can be effectively prohibited by marking the constructor with explicit
class Number{ float val; public: explicit Number(float inVal); float pubVal; }; int main(){ Number num{3.14}; num = 1.414; //Compilation failed printNumber(0.618);//Compilation failed //... }
3. const and mutable
As the name suggests, they are constants and variables respectively. The former requires that member functions cannot modify member variables of classes
class Number{ float val; public: mutable float pubVal; //Note that mutable is used for this variable explicit Number(float inVal); void printVal() const; //This method uses const }; void Number::printVal() const{ cout<<val<<endl; /* val = val+1; //This is not allowed */ pubVal = val+1; //This is allowed }
That is, const members can only modify mutable members.
4. Self reference
Self reference is a programming skill. For functions that change the state of a class, if you take the class itself as the return value, you can achieve cool and elegant chain operations.
class Number{ float val; public: explicit Number(float inVal); Number& addOne(); //Its return value is the address of the current object }; Number& Number::addOne(){ cout<<val++<<endl; return *this; }
Where, * this points to the object that calls the member function. Test it
int main(){ Number num{3.14}; //Equivalent to reference constructor num.addOne().addOne().addOne(); system("pause"); return 0; }
The result is
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 4.14 5.14 Please press any key to continue. . .
5. static
As the name suggests, static members are called static because they have only one storage location. For a class, no matter how many instances are created, the static variables in the class are stored only in that location. This means that the static member has a longer life cycle than the object instance. When an object is destroyed, the static member is not destroyed, so when it is called again, there is no need to allocate memory.
class Number{ float val; static Number defaultNum; public: explicit Number(float inVal=0); static void setDefault(float inVal); void printVal() const; }; void Number::printVal() const{ cout<<val<<endl; } //Define default Num Number Number::defaultNum{3.14}; void Number::setDefault(float val){ defaultNum = Number{val}; }; Number::Number(float inVal){ val = inVal ? inVal : defaultNum.val; } int main(){ Number num{}; //Equivalent to reference constructor num.printVal(); system("pause"); return 0; }
Output as
PS E:\Code\cpp> .\a.exe 3.14 Please press any key to continue. . .
Realization of complex number
The complex number has real part and imaginary part, and the default value is 0. Its addition and subtraction are the subtraction of real part and imaginary part respectively, and its multiplication is
#include<iostream> using namespace std; class Complex{ float real; //real part float im; //imaginary part static Complex defaultNum; public: explicit Complex(float inReal=0, float inIm=0); static void setDefault(float inReal, float inIm); void printVal() const; Complex& add(float inReal, float inIm); Complex& minus(float inReal, float inIm); Complex& multi(float inReal, float inIm); Complex& div(float inReal, float inIm); }; //The default is {0, 0} Complex Complex::defaultNum{0,0}; void Complex::setDefault(float inReal,float inIm){ defaultNum = Complex{inReal, inIm}; }; //Print current value void Complex::printVal() const{ cout<<"real part: "<<real<<endl; cout<<"image part:"<<im<<endl; } //addition Complex::Complex(float inReal, float inIm){ real = inReal ? inReal : defaultNum.real; im = inIm ? inIm : defaultNum.im; } Complex& Complex::add(float inReal, float inIm){ real += inReal ? inReal : 0; im += inIm ? inIm : 0; return *this; } Complex& Complex::minus(float inReal, float inIm){ real -= inReal ? inReal : 0; im -= inIm ? inIm : 0; return *this; } Complex& Complex::multi(float inReal, float inIm){ float temp = real*inReal - im*inIm; im = real*inIm + im*inReal; real = temp; return *this; } Complex& Complex::div(float inReal, float inIm){ float temp = inReal*inReal + inIm*inIm; float tempReal = (real*inReal + im*inIm)/temp; im = (im*inReal-real*inIm)/temp; real = tempReal; return *this; } int main(){ Complex num{}; //Equivalent to reference constructor num.add(1,2).multi(3,4).div(1,2); num.printVal(); system("pause"); return 0; }
The following operations are based on this complex class.
6. Member function overloading
For the above addition, subtraction, multiplication and division, the default input value is the combination of real part and imaginary part, but the operation of two complexes cannot be realized. C + + supports overloading of member functions.
class Complex{ /* The end of the class defined above */ Complex operator+(Complex); Complex operator-(Complex); Complex operator*(Complex); Complex operator/(Complex); //Realize similar number multiplication function Complex operator*(float); Complex operator/(float); } These functions can be defined in the simplest way Complex& Complex::add(Complex num){ real += num.real; im += num.im; return *this; } You can also call a defined member function Complex& Complex::multi(Complex num){ multi(num.real, num.im); return *this; }
7. Operator overloading
In C + +, some operators can be easily overloaded. The format is
Complex operator+(Complex);
1
For the two complex numbers a and B, the operator a+b after calling the overload is equivalent to a.operator(b).
Its specific implementation is
class Complex{ /* The end of the class defined above */ Complex operator+(Complex); Complex operator-(Complex); Complex operator*(Complex); Complex operator/(Complex); } Complex Complex::operator+(Complex num){ float outReal = real+num.real; float outIm = im+num.im; return Complex{outReal, outIm}; } Complex Complex::operator-(Complex num){ return Complex{real-num.real, im-num.im}; } Complex Complex::operator*(Complex num){ return Complex{real*num.real - im*num.im, real*num.im + im*num.real}; } Complex Complex::operator/(Complex num){ float temp = num.real*num.real + num.im*num.im; return Complex{(real*num.real + im*num.im)/temp, (im*num.real-real*num.im)/temp}; } Complex Complex::operator*(float val){ return Complex{real*val,im*val}; } Complex Complex::operator/(float val){ return Complex{real/val,im/val}; } //Main function int main(){ Complex temp{1,1}; Complex temp1 = temp-temp*temp*2; temp1.printVal(); temp.printVal(); system("pause"); return 0; }
The test result is
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe real part: 1 image part:-3 real part: 1 image part:1
It can be seen that although the operator is overloaded, the operation order is preserved.
8.new
In C language, the memory is dynamically opened up by means of STRUCT* struct = (STRUCT*)malloc(sizeof(STRUCT)) for future use. In C + +, new can do this job.
for example
int* p = new int; int* Q = new int(5); 1 2 about Complex Class, which can be implemented in the form of pointer int main(){ Complex* temp = new Complex(1,1); temp->add(*temp); temp->printVal(); delete(temp); //Destroy temp memory system("pause"); return 0; }
Among them, -> is also derived from C language. It is used to call class members with class pointers. The result is
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe real part: 2 image part:2 Please press any key to continue. . .
9. Destructor
Generally, new is used to allocate memory space. After the call, delete is used to release memory. During the execution of delete, the destructor will be called.
Before explaining the destructor, you need to review the constructor. The so-called constructor is a function with the same class name. Through the constructor, you can create a class object and open up enough memory. The destructor is the destruction function, which destroys the memory opened by the constructor.
The destructor is also the same as the class name. It has no parameters, no return and cannot be overloaded. It is not easy to understand but easy to use.
public: explicit Complex(float inReal=0, float inIm=0); //This is the destructor, ~Complex(){}
10.friend
In complex classes, real and imaginary parts are encapsulated as private variables, and external functions are inaccessible. At this time, if you want to create a variable to extract the real or imaginary part of a complex number in other classes, you can consider the friend mechanism.
The so-called friend mechanism allows a class to authorize its non common members to the specified function or class, which is modified by the keyword friend. For example,
/* Complex class */ friend float getReal(Complex num); }; float getReal(Complex num){ cout<<num.real<<endl; return num.real; }
In this way, getReal can directly access the private members of the Complex class.
10. Inheritance of classes
Generally speaking, the complex a + b i a+b\text{i}a+bi does not support operations similar to direct multiplication, that is, (a + b i) * (a + b i)= ̸ a b + c d i (a+b\text{i})*(a+b\text{i})\not ={ab+cd\text{i}}(a+bi)∗(a+bi)
=ab+cdi, if you want to construct a new algebraic relationship to support both complex multiplication and direct multiplication, you need to create a new class. In order to avoid excessive code duplication, this class can exist as a derived class of complex class.
It should be noted that the default members of the previously created Complex class are private, so its im and real are inaccessible to subclasses. For simplicity, we change the class to struct so that its subclasses can be called painlessly.
//Derived class of Complex class class antiComplex : Complex{ public: antiComplex(float inReal,float inIm){ real = inReal; im = inIm; }; void printVal(); antiComplex operator*(antiComplex); }; antiComplex antiComplex::operator*(antiComplex num){ return antiComplex{real*num.real,im*num.im}; } //Override printVal function void antiComplex::printVal(){ cout<<"I'm antiComplex"<<endl; cout<<"real part: "<<real<<endl <<"image part:"<<im<<endl; } int main(){ antiComplex temp{1,2}; temp.printVal(); temp = temp*temp; temp.printVal(); system("pause"); return 0; }
The result is
PS E:\Code\cpp> .\a.exe I'm antiComplex real part: 1 image part:2 I'm antiComplex real part: 1 image part:4 Please press any key to continue. .
.
In C + +, there are three inheritance methods: public, private and protected,
Generally, the default is public inheritance, which is characterized by the inability to access the private members of the parent class;
private inheritance cannot even access public members and protected members;
protected allows its subclasses to access, but does not allow subclasses of subclasses to access.
The specific performance is shown in the following table. It is read as public member, which is expressed as private member during private inheritance.
public inherit private inherit protected inherit public member public private protected private member private private private protected member protected private protected
11. Polymorphism
Polymorphism is the difference when multiple subclasses inherit a base class. For example, Complex and antiComplex can be used as subclasses of an abstract data structure. After all, they are only different in multiplication and division.
#include<iostream> using namespace std; struct Abstract{ float real; float im; Abstract(float inReal, float inIm){ real = inReal; im = inIm; } void printVal(){ cout<<"I'm Abstract"<<endl; }; Abstract& multi(Abstract val){}; }; struct Complex:Abstract{ Complex(float inReal, float inIm) :Abstract(inReal,inIm){} void printVal(); Abstract& multi(Abstract val); }; void Complex::printVal(){ cout<<"I'm Complex:" <<real<<"+"<<im<<"i"<<endl; } Abstract& Complex::multi(Abstract val){ float temp = real*val.real - im*val.im; im = real*val.real + im*val.im; real = temp; return *this; } struct antiComplex:Abstract{ antiComplex(float inReal, float inIm) :Abstract(inReal,inIm){} void printVal(); Abstract& multi(Abstract val); }; void antiComplex::printVal(){ cout<<"I'm antiComplex:" <<real<<"+"<<im<<"j"<<endl; } Abstract& antiComplex::multi(Abstract val){ real = real*val.real; im = im*val.im; return *this; } int main(){ Complex temp{1,2}; antiComplex antemp{1,2}; temp.multi(temp).multi(temp); antemp.multi(antemp).multi(temp); temp.printVal(); antemp.printVal(); system("pause"); return 0; }
The output result is
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe I'm Complex:-3+5i I'm antiComplex:1+4j Please press any key to continue. . .
It can be seen that this result is wrong because the return pointer of the multi function is of Abstract type, and the multi in the base class is an empty function, so multi is executed only once. Therefore, if the subclass continues to maintain the subclass's methods after calling the function again, you need to use the keyword virtual.
12.virtual
It is more appropriate to explain the function of virtual directly through pointers.
struct Abstract{ float real; float im; Abstract(float inReal, float inIm){ real = inReal; im = inIm; } void printVal(){ cout<<"I'm Abstract"<<endl; }; }; struct Complex:Abstract{ Complex(float inReal, float inIm) :Abstract(inReal,inIm){} void printVal(){ cout<<"I'm Complex:" <<real<<"+"<<im<<"i"<<endl; } }; int main(){ Abstract* a; Complex temp{1,2}; a = &temp; a->printVal(); system("pause"); return 0; }
The operation result is
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe I'm Abstract
That is, although the pointer of the parent class points to the object of the child class, the final pointer still points to the function of the parent class. If the virtual keyword is added to the parent function, it will be changed to
struct Abstract{ float real; float im; Abstract(float inReal, float inIm){ real = inReal; im = inIm; } virtual void printVal(){ cout<<"I'm Abstract"<<endl; }; };
The output is PS E:\Code\cpp> .\a.exe I'm Complex:1+2i
It can be seen that the virtual function is used to make the parent class pointer point to the method of the actual object. The of the parent class multi The function becomes
virtual Abstract& multi(Abstract val){};
The final output is
PS E:\Code\cpp> .\a.exe I'm Complex:-16+34i I'm antiComplex:-16+136j