A detailed explanation of assigning derived classes to base classes based on C/C++

Table of contents

Assign derived class object to base class object

Assign derived class pointer to base class pointer

Assign derived class reference to base class reference

Recommended bibliography for this issue

 


Data type conversion often occurs in C/C++. For example, when assigning int type data to a float type variable, the compiler will first convert the int type data into a float type before assigning a value; conversely, the float type data It can also be assigned to a variable of type int after type conversion.

The premise of data type conversion is that the compiler knows how to trade off the data. For example:

int a = 10.9;
printf("%d\n", a);

 The output result is 10, and the compiler will discard the decimal part directly (not round up). Another example:

float b = 10;
printf("%f\n", b);

The output is 10.000000, and the compiler automatically adds the decimal part.

A class is actually a data type, and data type conversion can also occur, but this conversion is only meaningful between the base class and the derived class, and only the derived class can be assigned to the base class, including the assignment of derived class objects to Base class object, assign derived class pointer to base class pointer, assign derived class reference to base class reference, this is called Upcasting in C++. Correspondingly, assigning a base class to a derived class is called downcasting.

Upcasting is very safe and can be done automatically by the compiler; downcasting is risky and requires manual intervention by the programmer. This section only introduces upward transformation, and downward transformation will be introduced in subsequent chapters.

Upcasting and downcasting are common concepts in object-oriented programming, and they also exist in programming languages ​​such as Java and C#.

 

Assign derived class object to base class object

The following example demonstrates how to assign a derived class object to a base class object:

#include <iostream>
using namespace std;

//基类
class A{
public:
    A(int a);
public:
    void display();
public:
    int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
    cout<<"Class A: m_a="<<m_a<<endl;
}

//派生类
class B: public A{
public:
    B(int a, int b);
public:
    void display();
public:
    int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){
    cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}


int main(){
    A a(10);
    B b(66, 99);
    //赋值前
    a.display();
    b.display();
    cout<<"--------------"<<endl;
    //赋值后
    a = b;
    a.display();
    b.display();

    return 0;
}

Running result:
Class A: m_a=10
Class B: m_a=66, m_b=99
----------------------------
Class A: m_a=66
Class B: m_a=66, m_b=99

In this example, A is the base class, B is the derived class, and a and b are their objects respectively. Since the derived class B contains members inherited from the base class A, Therefore, the derived class object b can be assigned to the base class object a. It can also be found through the running results that the value of the member variable contained in a has changed after the assignment.

The essence of assignment is to write the existing data into the allocated memory. The memory of the object only contains member variables, so the assignment between objects is the assignment of member variables, and there is no assignment problem in member functions. The running results also strongly prove this point. Although there is a=b;such an assignment process, a.display() always calls the display() function of class A. In other words, assignments between objects do not affect member functions, nor the this pointer.

When assigning a derived class object to a base class object, the newly added members of the derived class will be discarded, that is, "overkill", as shown in the following figure:

 It can be found that even if the derived class object is assigned to the base class object, the base class object will not contain the members of the derived class, so it is still different to access the members of the derived class through the base class object. For the example above, a.m_a is true, but a.m_b is false because a does not contain member m_b.

This conversion relationship is irreversible, and only derived class objects can be used to assign values ​​to base class objects, but base class objects cannot be used to assign values ​​to derived class objects. The reason is simple, the base class does not contain the member variables of the derived class, and cannot assign values ​​to the member variables of the derived class. Similarly, assignments cannot be made between objects of different derived classes of the same base class.

To understand this problem, we have to start with the essence of assignment. The assignment is actually to fill the memory with data. When there is a lot of data, it is easy to handle and just discard it; in this example, when b is assigned to a (executing the a=b;statement), the member m_b is redundant and will be directly discarded, so it will not happen Assignment error. But when there is less data, the problem is very difficult, the compiler does not know how to fill the remaining memory; if there is b= a;such a statement in this example, the compiler does not know how to assign a value to the variable m_b, so an error occurs.

Assign derived class pointer to base class pointer

In addition to assigning derived class objects to base class objects (assignment between object variables), you can also assign derived class pointers to base class pointers (assignment between object pointers). Let's first look at an example of multiple inheritance. The inheritance relationship is:

 

#include <iostream>
using namespace std;

//基类A
class A{
public:
    A(int a);
public:
    void display();
protected:
    int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
    cout<<"Class A: m_a="<<m_a<<endl;
}

//中间派生类B
class B: public A{
public:
    B(int a, int b);
public:
    void display();
protected:
    int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){
    cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}

//基类C
class C{
public:
    C(int c);
public:
    void display();
protected:
    int m_c;
};
C::C(int c): m_c(c){ }
void C::display(){
    cout<<"Class C: m_c="<<m_c<<endl;
}

//最终派生类D
class D: public B, public C{
public:
    D(int a, int b, int c, int d);
public:
    void display();
private:
    int m_d;
};
D::D(int a, int b, int c, int d): B(a, b), C(c), m_d(d){ }
void D::display(){
    cout<<"Class D: m_a="<<m_a<<", m_b="<<m_b<<", m_c="<<m_c<<", m_d="<<m_d<<endl;
}


int main(){
    A *pa = new A(1);
    B *pb = new B(2, 20);
    C *pc = new C(3);
    D *pd = new D(4, 40, 400, 4000);

    pa = pd;
    pa -> display();

    pb = pd;
    pb -> display();

    pc = pd;
    pc -> display();

    cout<<"-----------------------"<<endl;
    cout<<"pa="<<pa<<endl;
    cout<<"pb="<<pb<<endl;
    cout<<"pc="<<pc<<endl;
    cout<<"pd="<<pd<<endl;

    return 0;
}

Running results:
Class A: m_a=4
Class B: m_a=4, m_b=40
Class C: m_c=400
-----------------------
pa= 0x9b17f8
pb=0x9b17f8
pc=0x9b1800
pd=0x9b17f8

In this example, multiple object pointers are defined, and an attempt is made to assign derived class pointers to base class pointers. Different from the assignment between object variables, the assignment between object pointers does not copy the members of the object, nor does it modify the data of the object itself, but only changes the pointing of the pointer.

1) Access the members of the derived class through the base class pointer

Please pay attention to the code on line 68 first. We assign the derived class pointer pd to the base class pointer pa. From the running results, we can see that although the member variable of the derived class is used when calling the display() function, the display() function itself is the base class. That is to say, when assigning a derived class pointer to a base class pointer, only the member variables of the derived class can be used through the base class pointer, but the member functions of the derived class cannot be used. This seems a bit nondescript. Why? Lines 71 and 74 are similar.

pa was originally a pointer of base class A, but now it points to an object of derived class D, which changes the implicit pointer this, and also points to an object of class D, so the object of class D is finally used inside display() Member variables, I believe this is not difficult to understand.

Although the compiler accesses the member variable through the pointing of the pointer, it does not access the member function through the pointing of the pointer: the compiler accesses the member function through the type of the pointer. For pa, its type is A, no matter which object it points to, the member function of class A is used

In a nutshell: the compiler accesses member variables through pointers, and uses the data of which object the pointer points to; the compiler accesses member functions through the type of the pointer, and uses the function of which class the pointer belongs to.

2) Inconsistent values ​​after assignment

In this example, we assign the pointer pd of the final derived class to the base class pointers pa, pb, and pc respectively. It stands to reason that their values ​​should be equal and point to the same block of memory, but the running results strongly refute this inference. Only the values ​​of the three pointers pa, pb, and pd are equal, and the value of pc is greater than all of them. That is, pc = pd;after the statement is executed, the values ​​of pc and pd are not equal.

 

Assign derived class reference to base class reference

References are essentially realized through pointers. Since the pointer of the base class can point to the object of the derived class, then we have reason to infer that: the reference of the base class can also point to the object of the derived class, and its performance and pointer are akin.

Modify the code inside the main() function in the above example to use references instead of pointers:

int main(){
    D d(4, 40, 400, 4000);
   
    A &ra = d;
    B &rb = d;
    C &rc = d;
   
    ra.display();
    rb.display();
    rc.display();

    return 0;
}

 Running results:
Class A: m_a=4
Class B: m_a=4, m_b=40
Class C: m_c=400

ra, rb, rc are references of the base class, they all refer to the derived class object d, and call display( ) function, it can be found from the running results that although the member variable of the derived class object is used, the member function of the derived class is not used, which is the same as the performance of the pointer.

The reason why the performance of references and pointers is so similar is that there is no essential difference between references and pointers, and references are simply encapsulation of pointers

Finally, it should be noted that after the upward transformation, the objects, pointers, and references of the base class can only access the members inherited from the base class (including member variables and member functions), and cannot access the newly added members of the derived class.

Recommended bibliography for this issue

 book donation rules

Until June 9th at 6pm

Draw will be held on June 9th at 7pm

Favorite + like + comment (Life is short, refuse to introvert!) Each person can post up to 5 comments

The program randomly selects 2 lucky partners + the highest number of comments and likes to give a physical book!

 

 

 

 

        Action on Code: Learning Python Programming with Zero Basics (ChatGPT Edition) Introduces the basics of the Python programming language from the shallower to the deeper, and is an introductory tutorial for zero-based programming learners. The book has 17 chapters, of which chapters 1 to 9 are the basics, introducing the language foundation of Python, including environment installation, input/output variables, common data types, mathematical and logical operations, conditional judgment and loop statements, compound data types, Functions, modules, and file operations; Chapters 10 to 13 are advanced chapters, which introduce the expanded knowledge related to Python programming, including Excel table data processing, information matching using regular expressions, object-oriented programming design, multi-threading and multi-threading. The process; Chapters 14~16 are actual combat articles, which introduce 3 actual combat projects, namely, using requests to develop web crawlers, using tkinter to develop GUI calculators, and using pygame to develop aircraft war games; Chapter 17 is ChatGPT, which mainly introduces How beginners can use the most popular AI tool ChatGPT to learn Python programming. The content of this book is systematic and comprehensive, with rich cases and easy-to-understand explanations. It is not only suitable for beginners with zero basic knowledge of Python, but also suitable as a textbook for relevant majors in secondary and higher vocational colleges. In one sentence, I recommend the introductory classic tutorial for Xiaobai to learn Python programming, combined with the ChatGPT application, so that you can learn it, use it, and do it! Author brief introduction Yuan Xin (Crossin) has a bachelor's degree in software engineering from Nanjing University and a master's degree in computer science from Shanghai Jiaotong University. He has experience in Internet finance, VR/AR, games and other industries. He was invited as a lecturer at the Python China Developers Conference and an expert in Tencent Cloud Classroom Review. In 2013, he founded the programming learning self-media "Crossin's Programming Classroom", with more than 300,000 readers on all platforms. Jia Wei, a senior engineer, has in-depth research on various development languages, focusing on Python artificial intelligence development, data analysis, and machine learning, and has rich experience in education and training. Jingdong purchase link: https://item.jd.com/13951968.html

Guess you like

Origin blog.csdn.net/m0_64122244/article/details/131073838