Inheritance and derivation
C ++ is object-oriented programming, so as long as object-oriented, will have polymorphic, inherited characteristics. How does C++ implement inheritance? Look at the following section. Inheritan**ce ** can be understood as the process by which one class obtains member variables and member functions from another class. For example, class B inherits from class A, then B has member variables and member functions of A.
Derive ** is the same concept as inheritance in C++, just from a different perspective. Inheritance is the son taking over the father’s property, and derivation is the father passing on the property to the son.
An inherited class is called a parent or base class, and an inherited class is called a subclass or derived class. “Subclasses” and “parent classes” are often referred to together, and “base classes” and “derived classes” are often referred to together.
In C++ inheritance is called derived class, base class hatches except derived class, use: to indicate that the subclass inherits the parent class, C++ supports multiple inheritance, using comma separation
class Parent {
public:
int name;
protected:
int code;
private:
int num;
};
class Parent1 {
};
// in C++, : represents inheritance, which can be separated by commas
/ / public/protected/private inheritance, in the base class has some protection mechanism is private inheritance by default
class Child : public Parent, Parent1 {
void test(a) {
// Derived classes can access both public and protected properties
this->name;
this->code; }};Copy the code
C++ has added public, protected, and private derivations to its derived classes, and the default is private
Class Derived class name: [inheritance method] base class name {
The newly added member of a derived classCopy the code
};
//private Private inheritance
class Child1 : private Parent {
void test(a) {
this->name;
this->code; }};/ / protected inheritance
class Child2 : protected Parent {
void test(a) {
this->name;
this->code; }};Copy the code
Public, protected, and private derivations limit and protect the properties and methods of the created object that call the parent class
Child child;
child.name;/ / public inheritance. The caller has access to the parent public property, but not to the private property
Child1 child1;
// child1.name; / / private inheritance. The caller does not have access to the parent public and private properties
Child2 child2;
// child2.name; //protected inheritance. The caller does not have access to the public and private attributes of the parent class
Copy the code
Virtual functions
** key!! ** inheritance in C++ differs from inheritance in Java: base-class member functions and derived member functions do not constitute overloading
Base class members and derived class members of the same name will cause masking, this sentence for the member variable is very good to understand, for the member function to pay attention to, no matter how the function parameter, as long as the same name will cause masking. In other words, base-class member functions and derived member functions do not constitute overloading, and if the derived class has a function with the same name, then all functions in the base class with the same name will be masked, regardless of whether their arguments are the same.
The parent class code is as follows:
class Person {
protected:
char *str;
public:
Person(char *str) {
if(str ! =NULL) {
this->str = new char[strlen(str) + 1];
strcpy(this->str, str);
} else {
this->str = NULL;
}
cout << "parent" << endl;
}
Person(const Person &p) {
cout << "copy parent" << endl;
}
void printC(a) {
cout << "parent printC" << endl;
}
~Person() {
// if (str ! = NULL) {
// delete[] str; // The destructor is called only once if this method is called
/ /}
// cout << "parent destroy" << endl;}};Copy the code
Subclasses inherit from the parent class and call the constructor of the parent class, using: to call the constructor of the parent class
/ / subclass
class CTest : public Person {
public:
// Call the constructor of the parent class
CTest(char *str) : Person(str) {
cout << "child" << endl;
}
void printC(a) {
cout << "child printC" << endl;
}
~CTest() {
cout << "child destroy "<< endl; }};Copy the code
The difference between C++ and Java is the following code: any pointer to the parent class is the method of the calling parent class. Even if the object of the child class is directly assigned to the parent class, the method of the parent class will be called, but not the method of the child class.
Person person = CTest("jake");
person.printC(a);//parent printC
cout << "-- -- -- -- -- -- -- -- -- -- -" << endl;
Person *p = NULL;
CTest c1("123");
p = &c1;
c1.printC(a);//child printC
p->printC(a);//parent printC calls the parent method.
Copy the code
Even through pointer passing and reference passing, any parent class used will call the parent class’s methods
// Only methods of the parent class will be called through the pointer
void howToPaint(Person *p) {
p->printC(a); }// By referring to a type, only methods of the parent class are called, not methods of the child class
void howToPaint1(Person &p) {
p.printC(a); } cout <<"-- -- -- -- -- -- -- -- --" << endl;
howToPaint(p);//parent printC
howToPaint(&c1);//parent printC
cout << "-- -- -- -- -- -- --" << endl;
Person p1("123");
// are methods of the parent class
howToPaint1(p1);//parent printC
howToPaint1(c1);//parent printC
cout << "-- -- -- -- -- -- -- --" << endl;
CTest c2("123");
Person p2 = c2;// Will the parent copy function be called? Copy parent makes the call
Copy the code
Why is that? The memory model is shown in the following figure:
C ++ is called in the order of the function table, so it is obvious that the superclass functions come before the subclass functions
So how do you call a subclass’s method? C ++ provides the means for virtual functions, which are also the key to achieving polymorphism.
Virtual function and pure virtual function, pure virtual function in Java abstract == pure virtual function
In practice, once we define a destructor, we want to use it to clean up the object when it is destroyed, such as freeing memory, closing files, etc. If the class is also a base class, we must declare the destructor virtual, otherwise we risk memory leaks. That is, the destructor of the base class should be declared virtual in most cases.
Classes that contain pure virtual functions are called Abstract classes. It is abstract because it cannot be instantiated, that is, it cannot create objects. For obvious reasons, pure virtual functions have no body, are not complete functions, cannot be called, and cannot be allocated memory.
Abstract classes are usually used as base classes, leaving derived classes to implement pure virtual functions. Derived classes must implement pure virtual functions to be instantiated.
- A pure virtual function can make a class an abstract base class, but an abstract base class can contain other member functions (virtual or normal) and member variables in addition to pure virtual functions.
- Only virtual functions in a class can be declared as pure virtual functions. Neither ordinary member functions nor top-level functions can be declared as pure virtual functions.
- The destructor of the base class must be declared virtual.
#include <iostream>
using namespace std;
class Person {
public:
// Add a pointer to the virtual table
virtual void look(a) {// Virtual subclasses can overwrite functions
cout << "virtual look" << endl;
}
virtual void speak(a) {};// Pure virtual functions must be implemented by subclasses
// The destructor of the base class must be declared virtual
virtual ~Person() {
cout << "~Person"<< endl; }};class Child : public Person {
public:
void speak(a) override {// Subclasses implement pure virtual functions
cout << "child speak" << endl;
}
void look(a) override {
cout << "child look" << endl;
Person::look(a);// Access the method of the parent class
}
~Child() {
cout << "~Child"<< endl; }};int main(a) {
Person *person = new Child(a);// Abstractions must be derived using Pointers rather than stacks
person->speak(a);//child speak
person->look(a);//child look
Person p;
cout << sizeof(p) << endl;*vtable *vtable *vtable *vtable *vtable *vtable
// Use the following code to prove it
typedef void (*func)(void);
func fun = NULL;
cout << (int*)&p << endl;// point to the first address of the function 0x61fdf8
cout << (int* (*)int*)&p << endl;// Address of the function 0x404560
fun = (func)*((int* (*)int*)&p);
fun(a);//virtual look
return 0;
}
/** * child speak child look virtual look ~Child ~Person */
Copy the code
The template
Templates are similar to Java generics. Template classes do not support separate declarations (.h) and implementations (.cpp). The root cause of “you cannot split the declaration and definition of a template into multiple files” is that instantiation of a template is done by the compiler, not the linker, which may result in instances not being found during linking.
- A function template
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
/** * Function templates are similar to generics in Java
// Method generics can only be declared on methods
template<typename T, typename R=int>
The default type of R is int
//typename == class Two equivalent
void swap2(T t, R r) {
}
template<typename T>
void swapT(T &a, T &b) {
cout << "swap: T a T b" << endl;
T temp = a;
a = b;
b = temp;
}
// Ordinary functions have higher priority than generic functions, only when the types overlap
void swapT(int &a, int &b) {
cout << "swap : int a int b" << endl;
int temp = a;
a = b;
b = temp;
}
int main(a) {
// Function templates
int a = 10;
int b = 20;
char c = 'a';
swapT<int>(a, b);// Display scheduling
swapT(a, b);// Automatically derive
// swap(a,c); // The error cannot be derived from the specific type
// swap2(); // The error cannot be derived from the specific type
return 0;
}
Copy the code
- Class template
// Template decorates the class
template<typename T, typename R>
class Person {
public:
T a;
R b;
Person(T t) {
}
T &getA(a) {
T t1;
// return t1; // This cannot be returned because the method will be destroyed after execution
return a;// The return value is reference}};/** * different from Java, more flexible than Java */
class Pp {
public:
void show(a) {
cout << "Pp show"<< endl; }};template<typename T>
class ObjTemp {
private:
T obj;
public:
void showPp(a) {
// Check automatically but unexpected errors occur
obj.show(a);
T is required in Java to call methods
}};template<typename T, typename R>
class CTest {
public:
T m_name;
R m_age;
CTest(T name, R age) {
this->m_name = name;
this->m_age = age;
}
void show(a) {
cout << "show T:" << m_name << " R:"<< m_age << endl; }};template<typename T, typename R>
void doWork(CTest<T, R> &cTest) {
cTest.show(a); }template<typename T>
void doWork2(T &t) {
t.show(a);
}
// The inheritance template problem is the same as Java
template<typename T>
class Base {
public:
T t;
};
// Determine the type or template
template<typename T, typename R>
class Son : Base<R> {
public:
T t1;
};
int main(a){
CTest<string, int> test("jake".28);//show T:jake R:28
doWork(test);
doWork2<CTest<string, int>>(test);// Display the call
doWork2(test);// Automatically derive
ObjTemp<Pp> temp;
temp.showPp(a);//Pp show can call the methods of the passed template
// Automatic type inference, which cannot be used on class templates to derive specific types
Person<int, string> p(100);
cout << p.getA() << endl;
}
Copy the code
ArrayList implements a java-like list of templates:
Note that the.h and.cpp separation we learned earlier does not support templates, and the parts of templates are merged into the.h file.
#include <iostream>
using namespace std;
#ifndef CPPDEMO_ARRAYLIST_H
#define CPPDEMO_ARRAYLIST_H
template<typename T>
class ArrayList {
public:
int d = 11;
ArrayList() {
this->size = 16;
this->realSize = 0;
this->arr = new T[this->size];
}
// Explicit cannot be called implicitly
explicit ArrayList(int capacity) {
this->size = capacity;
this->realSize = 0;
// Apply an array in the heap
this->arr = new T[this->size];// The space created in the heap stores an array of int[size], and arr points to the first address of the array
}
// Copy the function
ArrayList(const ArrayList &arrayList) {
this->size = arrayList.size;
this->realSize = arrayList.realSize;
this->arr = new T[arrayList.size];
// Assign the array value to the arR
for (int i = 0; i < this->size; ++i) {
this->arr[i] = arrayList.arr[i];//arrayList.arr[I] this->arr[I] is a pointer}}// destructor
~ArrayList() {
if (this->arr ! =nullptr) {
delete[] this->arr;
this->arr = nullptr; }}void add(T val) {
add(val, this->realSize);
}
void add(T val, int index) {
if (index < 0 || index > size) {
return;
}
// Determine whether the capacity is large enough for expansion
if (this->realSize >= size * 0.75) {
resize(a); }this->arr[index] = val;// equivalent to *((this->arr)+index) = val
this->realSize++;// Data size +1
}
T get(int index) {
if (index < 0 || index >= realSize) {
return - 1;
}
return this->arr[index];
}
T remove(int index) {
if (index < 0 || index >= realSize) {
return - 1;
}
// How to remove it? Cycle forward
int result = this->arr[index];
for (int i = index; i < size - 1; ++i) {
this->arr[i] = this->arr[i + 1];
}
this->realSize--;
// Determine the capacity reduction
return result;
}
//const is defined as constant
int getLength(a) const {
// realSize = realSize - 1; You can't modify all the variables inside the function
c = 11;// Mutable variables can be modified in constant functions
return realSize;
}
bool isEmpty(a) const {
return realSize == 0;
}
void resize(a) {
int netLength = size * 2;
T *p = new T[netLength];
// Copy data
for (int i = 0; i < size; ++i) {
*(p + i) = this->arr[i];
}
// Free the previous array
delete[] this->arr;
// reassign
this->arr = p;
this->size = netLength;
}
void toString(a) {
cout << "[";
for (int i = 0; i < realSize; ++i) {
cout << arr[i] << ",";
}
cout << "]" << endl;
}
private:
int size{};// Size of the container
int realSize{};// The true array length
T *arr;// We cannot use arrays here because array names are arR pointer constants and cannot be reassigned to arr. Pointers are pointer variables and array names are just pointer constants
mutable int c = 10;// Variables that can be modified in a mutable function need to be modified using mutable
};
#endif //CPPDEMO_ARRAYLIST_H
Copy the code
string
// String is unique to C++. String is an object that encapsulates the same string representation as C
string s1(a);
string s2("123");
string s3 = "wew";//string string is declared in the heap
string s4(4.'k');//4 K's constitute KKKK
string s5("123456".1.4);// Output four strings starting from 1:2345
cout << s4 << "" << s5 << endl;
s2.append(s3);/ / an extra 123 wew
s2.append(s3,1.2);//ew
cout << s2 << endl;//123wewew
string sub = s2.substr(2.3);// String clipping
cout << sub << endl;//3we
s4.swap(s5);// String swaps. Only references and addresses change external values
//c_str supports C and is converted to char *
string s = "jakeprim";// The destructor is removed from the heap after the method is executed
// This is not the case
const char *s_c = s.c_str(a);// Convert a C++ string to a C - enabled string, return a constant pointer
printf("%s\n", s_c);
FFmpeg uses C, so C conversion is necessary when using C++ development
char ss[20];
strcpy(ss, s.c_str());// Copy to a new variable
Copy the code