1. Classes and objects
1.1. C++ class definition
Defining a class is essentially defining a data type that contains members and operations.
classThe name of the class {Access modifiers:private/public/protectedData type member variable name; Return value Type Member function name (){}}Copy the code
Sample code:
#include <iostream>
#include <string>
using namespace std;
// Define a User class
class User {
private: // The following code has private access
string name; // Member variable 1
int age; // Member variable 2
public: // Access to the following code is public
Constructor, which assigns initial values to two members
User(const string &name, int age) {
this->name = name;
this->age = age;
}
// Get the value of the member variable
const string &getName(a) const {
return name;
}
// Get the value of the member variable
int getAge(a) const {
returnage; }};int main(a) {
// Instantiate a User object with a pointer to the heap memory address
User *user = new User("Xiao Ming".18);
// Get the values of two member variables of the User object
cout << user->getName() < <"" << user->getAge() << endl;
cout << sizeof(User) << endl;
// Release the heap memory occupied by the User object
delete user;
return 0;
}
Copy the code
Const string &getName() const {}
- The first one
const
Indicates that the return value is a constant. - The second
const
Indicates that the function does not and is not allowed to modify a member variable, and is calledConstant function.
1.1.1. Class access modifiers
Class access modifiers are used to encapsulate data and prevent functions from accessing internal members directly.
The modifier | role |
---|---|
public | Members of the publicIt is accessible outside the class and can read and write values of public member variables directly |
protected | Protected memberAccessed from a derived class (subclass) with the same effect as public; Is accessed in other classes and has the same effect as private |
private | Private membersThe values of public member variables cannot be read or written directly from outside the class |
Access modifier | This class | Derived classes | Other classes |
---|---|---|---|
public | ⭕ ️ | ⭕ ️ | ⭕ ️ |
protected | ⭕ ️ | ⭕ ️ | ❌ |
private | ⭕ ️ | ❌ | ❌ |
1.1.2. Constructors & destructors
A constructor is a special member function of a class that is executed each time a new object of the class is created.
The constructor name is exactly the same as the class name and does not return any type, nor does it return void, and can be used to set initial values for certain member variables.
// The default constructor has no arguments and no function body. Only used to create objects
User() {}// Pass parameters in the constructor to initialize the member
User(const string &name, int age) {
this->name = name;
this->age = age;
}
Copy the code
A destructor is a special member function of a class that is executed each time an object created is deleted.
The name of the destructor is exactly the same as the name of the class, prefixed by the tilde ~, and it does not return any values, nor can it take any arguments. Destructors help free resources before exiting the program, such as closing files, freeing memory, and so on.
// Release resources in destructor
~User() {
delete obj;
}
Copy the code
1.1.3. Static members
Use the static keyword to define a static member that is shared by all class objects.
class Test {
public:
// Declare a static member of the Test class
static int i;
};
// Initialize the static member of the Test class
int Test::i = 10;
int main(a) {
// Outputs a static member of the Test class
cout << " test1.i: " << Test::i << endl; / / [10]
return 0;
}
Copy the code
1.2. C + + functions
1.2.1. Copy constructor
The copy constructor is a special constructor that initializes a newly created object with a previously created object in the same class. Usually used:
- Initialize the newly created object by using another object of the same type.
- The copy object passes it as an argument to the function.
- Copy the object and return it from the function.
User(const User &user) {
this->name = user.name; // copy the value inside and assign it to the member
this->age = user.age; // copy the value inside and assign it to the member
}
Copy the code
The compiler generates a copy constructor by default. But if the class has pointer member variables and dynamic memory allocation, it must specify a copy constructor.
1.2.2. Friend functions
A friend function of a class is defined outside the class, but has access to all private and protected members of the class. Friend functions are declared in the class, but they are not member functions.
#include <iostream>
#include <string>
using namespace std;
class User {
private:
string name;
int age;
public:
User(const string &name, int age) {
this->name = name;
this->age = age;
}
// Declare a friend function
friend void setAge(User *user, int age);
// Declare a friend class
friend class Admin;
};
// Define friend functions
void setAge(User *user, int age) {
// Access the private member of the User class directly
user->age = age;
}
// Define a friend class
class Admin {
public:
void setName(User *user, const string &name) {
// Access the private member of the User class directlyuser->name = name; }};int main(a) {
User *user = new User("Xiao Ming".18);
cout << user->getName() < <"" << user->getAge() << endl; // 【 Xiaoming 18】
// Access private members through friend functions
setAge(user,20);
// Access private members through friend classes
Admin *admin = new Admin(a); admin->setName(user, "Xiao gang");
cout << user->getName() < <"" << user->getAge() << endl; // [Xiaogang 20]
delete user;
delete admin;
return 0;
}
Copy the code
1.2.3. Inline functions
If a function is inline, at compile time, the compiler places a copy of the function’s code wherever the function is called.
Inline functions are preceded by the keyword inline and need to be defined before they are called. If the function body is longer than one line, the compiler ignores the inline modifier.
inline int Max(int x, int y) {
return (x > y) ? x : y;
}
int main(a) {
cout << "Max (10,20): " << Max(10.20) << endl; / / [20]
return 0;
}
Copy the code
【 Advantages and disadvantages 】
- Reduce the overhead of function calls.
- Debugging information is usually more detailed and secure than macro definitions.
- May increase compilation time, as well as post-compilation volume.
2. Function and operator overloading
2.1. Function overloading
In the same scope, you can declare several functions with the same name but with different arguments of the same form (the number, type, or order of arguments), and you cannot override functions simply by returning different types.
#include <iostream>
using namespace std;
class printData {
public:
void print(int i) {
cout << "The integer is:" << i << endl;
}
void print(double f) {
cout << "Floating point numbers are:"<< f << endl; }};int main(void) {
printData pd;
// Output an integer
pd.print(5); // [integer: 5]
// Outputs a floating point number
pd.print(500.263); // [float: 500.263]
return 0;
}
Copy the code
2.2. Operator overloading
Overloaded operators are functions with special names consisting of the keyword operator and the operator symbol to be overloaded after it. Like other functions, overloaded operators have a return type and a list of arguments.
// Override the + operator to add two User objects
User operator+ (const User &user) {
User userResult;
userResult.name = this->name + user.name;
userResult.age = this->age + user.age;
return userResult;
}
int main(a) {
User *user1 = new User("Xiao Ming".18);
User *user2 = new User("Xiao gang".20);
// Add two objects using the overloaded + operator
User user3 = *user1 + *user2;
cout << user3.getName() < <"" << user3.getAge() << endl; // 【 Xiaoming Xiaogang 38】
return 0;
}
Copy the code
2.2.1. Operators can be overloaded
The operator | operation |
---|---|
Binocular arithmetic operator | + Plus,- Reduction,* By,/ In addition,% modulus |
Relational operator | = = Equal to,! = Is not equal to,< Less than,> More than,< = Less than or equal to,> = Greater than or equal to |
Logical operator | ` |
Unary operator | + Is,- Negative,* A pointer,& Take the address |
The increment and decrement operator | ++ Since the increase,-- Since the reduction of |
An operator | ` |
The assignment operator | = ,+ = ,- = ,* = ,/ = ,% = ,& = , ` |
Space application and release | new ,delete ,new[] ,delete[] |
Other operators | (a) Function call,-> Member access,. Comma,[] The subscript |
2.2.2. Operators cannot be overloaded
The operator | The instance |
---|---|
Member access operator | . |
Member pointer access operator | . ,-> |
Domain operators | : : |
Length operator | sizeof |
Conditional operator | ? : |
Preprocessing symbol | # |
3. The RVO and NRVO
RVO (Return Value Optimization) and NRVO (named Return Value Optimization) are compiler optimization techniques. To reduce the return of temporary objects created at the invocation of non-reference objects, as well as the number of copy constructs and destructors.
Let’s start with a phenomenon:
#include <iostream>
#include <string>
using namespace std;
class Test {
public:
string v;
Test add(const Test &s) const {
Test tmp;
tmp.v = this->v + s.v;
returntmp; }};int main(a) {
Test t1;
t1.v = "Hello";
Test t2;
t2.v = "World";
Test t3 = t1.add(t2);
cout << t3.v << endl;
return 0;
}
Copy the code
Running results:
HelloWorld
⭐️
Test tmp; Is a local variable that will be recycled after the function is run, causing the object to which Test t3 points to an exception. But it actually works, for the following reasons:
- When the function returns, the stack is added
tmp
Object through the copy constructorcopyGive me aTemporary objects(we can’t see), and then call the destructor collectiontmp
Object. Test t3 = t1.add(t2);
, in the=
When the number is assigned, theTemporary objects, again by copying the constructorcopytot3
, so that the results are preserved correctly.
In summary, the compiler makes two copies of the local variables that should have been destroyed on the stack, making the calculation correct.
However, different compilers have different optimizations for copy times.
- In VS, one copy (RVO) is performed in DEBUG mode and zero copy (NRVO) is performed in release.
- In a Clion environment, zero copies (NRVO) are performed.
- In an Xcode environment, zero copies (NRVO) are performed.
3.1. RVO
RVO optimizations pass in the function’s return value object as an argument at compile time and change the return value to void.
Original function and call method:
// The function takes one argument and returns a value
Test add(const Test &s) const {
Test tmp;
tmp.v = this->v + s.v;
return tmp;
}
// Call mode
Test t3 = t1.add(t2);
Copy the code
RVO optimized pseudo-code (actually more complex, pseudo-code easy to understand) :
// The return value is passed in as an argument, and the function does not need to return the value
void add(Test &result, const Test &t) const {
Test tmp;
tmp.v = this->v + t.v;
result = tmp;
}
// Call mode
Test t3;
t1.add(t3, t2);
Copy the code
After RVO optimization, only one copy (TMP object is copied to result) is required.
3.2. NRVO
NRVO will go one step further and not even create one.
Pseudo-code optimized by NRVO (actually more complex, pseudo-code is easy to understand) :
// The function does not need to return a value
void add(Test &result, const Test &t1, const Test &t2) const {
result.v = t1.v + t2.v;
}
// Call mode
Test t3;
t1.add(t3, t1, t2);
Copy the code
RVO optimization can be implemented without copying.
4. Packaging
Encapsulation is a concept in object-oriented programming that binds data together with functions that manipulate it, thus ensuring security from interference and misuse.
#include <iostream>
using namespace std;
class Adder {
public:
// constructor
Adder(int i = 0) {
total = i;
}
// External interface
void addNum(int number) {
total += number;
}
// External interface
int getTotal(a) {
return total;
};
private:
// External hidden data
int total;
};
int main(a) {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() << endl;
return 0;
}
Copy the code
5. Inheritance
Inheritance defines a new class based on a class to improve code reuse and execution efficiency.
Existing classes are called base classes (parent classes), and newly created classes are called derived classes (subclasses).
/ / the base class
class Animal {
/ / eat () function
/ / sleep () function
};
// Derived class (public inheritance)
class Dog : public Animal {
/ / bark () function
};
Copy the code
5.1. Inheritance type
- Public inheritance: The public members of a base class are also the public members of a derived class. The protected members of a base class are also protected members of a derived class. The private members of a base class cannot be accessed directly by a derived class, but can be accessed by calling the public and protected members of the base class.
- Protected inheritance: The public and protected members of the base class become protected members of the derived class.
- Private Inheritance (private) : Public and protected members of the base class become private members of the derived class.
Public inheritance is usually used, with little protected or private inheritance, but if the inheritance type is not specified, the default is private inheritance.
5.2. Multiple inheritance
Multiple inheritance means that a subclass can have more than one parent class, which inherits the characteristics of more than one parent class.
Code structure:
classDerived class name:Derived types1The base class name1, inherited type2The base class name2, {derived class body};Copy the code
More than base class inheritance type can be specified separately, not the same completely consistent.
5.3. Base & Derived Classes
A derived class inherits all of the base class methods except the following:
- The base class constructor, destructor, and copy constructor.
- An overloaded operator for the base class.
- Friend functions of the base class.
#include <iostream>
using namespace std;
/ / the base class
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
/ / a derived class
class Rectangle : public Shape {
public:
int getArea(a) {
// Use two members of the base class to calculate the area
return(Shape::width * Shape::height); }};int main(a) {
// Create a derived object
Rectangle rect;
// Call a function of the base class with a derived object
rect.setWidth(5);
rect.setHeight(7);
// Output the area of the object
cout << "Area:" << rect.getArea() << endl;
return 0;
}
Copy the code
6. Polymorphism
When a member function is called, different functions are executed depending on the type of object on which the function is called.
6.1. Static polymorphism
Static polymorphism (static linking) is when a function call is prepared before the program executes, that is, it only cares about the type of pointer, not what the pointer points to.
#include <iostream>
using namespace std;
/ / the base class
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
// The base class has a calculated area function
int getArea(a) {
cout << "Base class function called" << endl;
return (width * height);
}
protected:
int width;
int height;
};
/ / a derived class
class Rectangle : public Shape {
public:
// Derived classes also have a computed area function of the same name
int getArea(a) {
cout << "Derived class function called" << endl;
return(Shape::width * Shape::height); }};int main(a) {
// Polymorphic: Pointers to base classes point to derived objects
Shape *shape = new Rectangle(a);// Base class pointer calls function
shape->setWidth(5);
shape->setHeight(7);
// Output the area of the object
cout << shape->getArea() << endl;
return 0;
}
Copy the code
[Operation results]
The base class function to call
35
At compile time, it has been determined that shape->getArea() calls the getArea() function of the base class, that is, static polymorphism determination
6.2. Dynamic Polymorphism (Virtual Function)
Dynamic polymorphism (dynamic linking) refers to the function call is determined according to the pointer to determine, that is, only care about the pointer to the content, not the type of pointer.
6.2.1. Virtual functions
Is a function declared in the base class using the keyword virtual. When a virtual function in a base class is redefined in a derived class, the compiler is told not to statically link to that function. Use dynamic linking.
// The base class has a compute area virtual function
virtual int getArea(a) {
cout << "Base class function called" << endl;
return (width * height);
}
Copy the code
[Operation results]
Derived class function called
35
【 note 】
- Do not set base class constructors to virtual functions: compilation error
error: constructor cannot be declared 'virtual'
. - Base destructors are generally set to virtual functions: to prevent memory leaks in derived classes by only destructing base classes without destructing derived classes.
6.2.2. Pure virtual functions
A meaningful implementation of a virtual function is given in the base class so that the specific implementation of the function can be determined in the derived class. Pure virtual functions are specified by using = 0 in the declaration.
// Base class has a pure virtual function that computes area
virtual int getArea(a) = 0;
Copy the code
6.2.3. An abstract class
An abstract class is one in which at least one function is declared pure virtual.
- An abstract class cannot be used to instantiate an object; it can only be used as an interface. Attempting to instantiate an object of an abstract class results in a compilation error.
- Derived classes must override the pure virtual functions of the base class, or this will result in a compilation error.