www.cnblogs.com/wickedpries…
www.cnblogs.com/wickedpries…
preface
I plan to prepare some basic knowledge that will be asked in the interview, including C++, operating systems, computer networks, algorithms and data structures, etc. C++ starts with an in-depth exploration of the C++ object model. Unlike Effective C++, this book focuses on the underlying mechanics of C++ implementations, so I wrote this series assuming that the reader was already familiar with the basic C++ syntax (including classes, inheritance, polymorphism, generics, and so on), and will cover more details about how C++ implements them. This time I will write the first and second chapters and update the series with every second reading. If you have any questions, please feel free to discuss them in the comments section of this blog, or contact me at [email protected]. I am happy to discuss and improve with you.
Chapter one: About objects
In traditional C programs, procedural thinking is used: “data” and “operations (functions) that process data” are declared separately, and there is no correlation between the two. But in C++, there is a tendency to use a separate “abstract data type” (ADT) to encapsulate data. The structure of the code makes C++ seem more expensive in terms of time and space than C, but this is not necessarily the case. The main additional burden of LAYOUT and time in C++ compared to C is caused by Virtual, which falls into two parts:
1. The Virtual function mechanism is used to support an efficient “execution time binding”.
2. Virtual Base Class is used to realize “base classes that appear multiple times in the inheritance system and have a single and shared instance”.
Next, the book introduces three C++ object models: the simple object model, the table-driven object model, and the C++ object model.
In the C++ object model, non-nonstatic data members are assigned to each class object. Static data members and function members are stored outside the class object. The compiler generates a table for each class containing a bunch of Pointers to virtual functions. This table is called virtual Table (VTBL). Each class Object is inserted with a pointer to the associated virtual table, called VPTR. VPTR setup and reset are done automatically by the constructor, destructor, and copy Assignment operators. The Type_info object associated with each class is also stored in the Virtual table.
The advantage of this model is that it is more efficient in space and time; The disadvantage is that if the application code itself does not change and the nonstatic data members of the class Object used are changed, then the application code also needs to be recompiled (because the memory distribution has changed).
In the next paragraph, discussed the difference between struct and class in the book, for that matter can refer to this post: blog.csdn.net/omegayy/art…
In C++, unlike C, the memory distribution of objects is not necessarily the same as the order in which the members of a class are declared. For example, a C++ compiler might store protected data members before private data members, regardless of the order in which they are declared. Similarly, the layout of the data members of Base and Derived classes is not mandatory. However, data in the same Access section of a class is arranged in declarative order, but not necessarily in declarative order between different Access sections.
Later in the book, I spend some time explaining the mechanism of polymorphism, which I don’t think is too difficult for those familiar with C++ to understand, so I won’t repeat it here.
Chapter 2: Constructor semantics
This chapter mainly introduces the constructor generation mechanism in C++.
First, default constructor. Does a C++ compiler really generate default constructor for every class that does not declare a constructor? Not really. There are only four cases where the compiler must synthesize a default constructor for classes that have not been declared constructor:
1. Constructor has defualt constructor;
2. Base class has default constructor;
3. This class has virtual funciton;
4. This class has a Virtual Base class.
In synthesized Defualt constructor, only Base Class SubobJcets and Member Class Objects are initialized. All other nonstatic data members (int, int*, char, etc.) are not initialized.
Copy constructor comes next. As with Defualt Constructor, the compiler does not create copy Constructor for all classes that do not have user-defined copy Constructor. In the above four cases, the compiler synthesizes copy constructor for a class; Otherwise, the compiler executes bitwise Copy.
Next, NRV optimization and member Initialization List are introduced.
For NRV (Named Return Value) optimization, we can look at an example:
Suppose we have a function foo:
12345678 | X foo()``{`` ``X xx; `` ``if``(...) `` ``return xx; `` ``else`` ``return xx; ` `} |
---|
After optimization by the compiler, the code looks like this:
123456789101112 | void foo(X &result)``{`` ``result.X::X(); `` ``if``(...) `` ``{`` ``return``; `` ``}`` ``else`` ``{`` ``return``; ` ` ` `} ` `} |
---|
This saves the cost of default constructors, copy constructors, and destructors for temporary objects, thus helping to reduce runtime. In addition, it is important to note that NRV optimizations can have unintended consequences, such as when your class depends on the number of calls to the constructor or copy the constructor.
The first thing we need to know about the member Initialization List is when we need it:
1. Initialize a reference member;
When initializing a const member;
3. When a base class constructor is called and it has a set of arguments;
4. When a member class constructor is called and it has a set of arguments.
Note that the order of initialization in the list is determined by the order in which members of the class are declared, not by the order in the initialization list. This is a very easy place to go wrong.
Chapter 3: Data semantics
This chapter focuses on how data in a class is allocated in memory, including (multiple) inheritance and polymorphism.
Let’s start with a piece of code:
12345678 | class X{}; ``class Y :``virtual public X{}; ``class Z :``virtual public X{}; ``class A :``public Y, ``public Z{}; ``std::cout << ``sizeof``(X) << std::endl; ``std::cout << ``sizeof``(Y) << std::endl; ``std::cout << ``sizeof``(Z) << std::endl; ``std::cout << ``sizeof``(A) << std::endl; |
---|
The output on VS2013 is 1,4,4,8. Why is that? This involves the object model adopted by the compiler for C++ syntax. X is an empty class, but in order for its object to have an address, the compiler actually allocates 1 byte of space to its object, so its size is 1. Y and Z are virtual inheritors of X, and the compiler assigns them each A pointer to an object of X, so they have A size of 4. Similarly, it is multiple inheritors of Y and Z, so the compiler assigns two Pointers to it, so it has A size of 8. The memory distribution is shown in the figure:
The book also mentions a model that outputs 1,8,8,12. The idea of this model is to allocate 1 byte of space to each empty class, and given the alignment of 4 bytes, the results in the book can be derived and will not be repeated here.
Static data members are not placed in the layout of an object; static data members are not placed in the layout of an object. In the same access section(public, private, protected, etc.), members are arranged in declarative order, but not necessarily in consecutive order (due to alignment). At the same time, the data members of different Access sections are arranged freely, regardless of the order in which they are declared.
After that, the book discusses the performance of memory distribution in classes under single inheritance, polymorphic inheritance, multiple inheritance, and virtual inheritance, which is also the most important part of this chapter.
This is a very simple program:
12 | Point3d origin; ` ` origin. X = 0.0; |
---|
The time and space cost of executing this program varies with the nature of X. Let’s break it down into cases:
1. X is static data members
Each static member of the class has only one instance, stored in the data segment of the program, independent of the object. So in this case, access to X does not incur any additional burden in space or time.
2. X is nonstatic data members
When we access the nonstatic data members of a class, the compiler actually does the following:
1234 | // Source code ' 'origin. X =0.0; *(&origin+(&point3d ::x))=0.0; |
---|
That is, to get the address of X, the compiler needs to add origin’s starting address to x’s address offset. (Actually, at least in VS using &point3d ::x to represent offsets is problematic, but the idea is this.)
3. X is a base class variable without polymorphism and multiple inheritance
In the case of inheritance, this can lead to waste of space. Let’s look at an example:
This class contains one int and three chArs. If we declare these variables in a single class, the object size, including alignment, is 8 bytes.
Suppose we were to inherit:
Concrete3 objects will now be 16 bytes in size, 100% larger than the original design!
This is due to alignment, because in the C++ object model, in the memory distribution of an inherited class, each base class needs to follow alignment separately, resulting in a waste of space. The specific object layout can be seen below:
4. Add polymorphism
In this case, accessing the members of the class imposes additional burdens both in time and space, mainly in the following aspects:
1. Virtual table, used to store the address of each virtual function declared.
2. Each object will have a VPTR that provides a link to the execution period.
3. The compiler overrides constructor and destructor to create and delete VPTR.
5. Multiple inheritance
**** In the case of multiple inheritance, assignment between Pointers requires runtime evaluation. For example, the following inheritance structure:
We declare several objects and Pointers and assign them:
1234567 | Vertex3d v3d; ``Vertex *pv; ``Point2d *p2d; ``Point3d *p3d; ``pv=&v3d; ``p2d=&v3d; '``p3d-&v3d; |
---|
For p3D and p2D assignments, simply assign the address of v3D directly. For pv assignment, the compiler computes a Vertex offset in Vertex3d from which to obtain pv’s address. Because the memory distribution between classes looks like this:
The starting address of the Vertex part of Vertex3d is not the starting address of the Vertex3d object, so assigning pv requires a run-time overhead.
6. Virtual inheritance
**** in virtual inheritance, the C++ object model divides classes into two regions. One is an immutable region, which is stored directly in objects. One is the shared region, which stores the Virtual Base class Subobjects, which is stored somewhere in memory separately, and the Derived Class Object holds Pointers to it. In the Cfront compiler, Pointers are inserted into each Derived Class Object, each pointing to a virtual Base class, at a cost in time and space. As follows:
123456789101112 | // Vertex and Point3d virtually inherit Point2d ' 'void Point3d::operator+=(``const Point3d&rhs)``{`` ``x+=rhs.x; `` ``y+=rhs.y; `` ``z+=rhs.z; ` `} _vbcPoint2d->x+=rhs._vbcPoint2d->x; ``_vbcPoint2d->y+=rhs._vbcPoint2d->y; ``z+=rhs.z; |
---|
This is only the most basic solution, but the book also suggests some compiler optimization methods for time and space, you can read more.
Finally, the book discusses how to obtain the address offset of a member of a class. I’ll summarize two approaches here:
1.((int)&((structure*)0)->member)
Int Test::* pOffset = &test ::x; (*(void**)(&pOffset)));
= = = = = = =
Dsqiu.iteye.com/blog/166961…
I had been completely blank about The internal principles of C++ before, and then I found Inside The C++ Object Model, which I learned a lot. Because it was written earlier, some knowledge should be updated, but it is still worth studying carefully, because The content of The book is rather scattered. So always want to find a time to sort it out, then into this article, although are copy the book, but let me have a review of the old to know the new consciousness, there are three good data in the vicinity, shared together! September 2, 2012 4:31 am Thank you Zhang Yusheng singing company!
Study notes for Inside The C++ Object Model
Chapter one is about objects
Layout costs with class encapsulation:
Classes do not add cost; data members are directly embedded in every class object, just like C structs. Member functions are included in the class declaration, but not in Object. Each non-inline function produces only one function entity. As for the inline function, a function entity is generated where each call is used (the function body is expanded at the call point).
The major additional burdens of class layout and access time are imposed by Virtual, including:
The Virtual function mechanism is used to support an efficient Runtime binding.
Virtual Base classes are used to implement “base classes that appear multiple times in the inheritance system and have a single shared entity”.
There is, of course, the additional burden of chopping inheritance between “a derived class and its second or subsequent base class.”
C++ object model
In the C++ object model, nonstatic data members are placed in every class object, and static data members are stored outside all class objects. Static and nonstaitc functions are also placed outside all class Objects. Virtual functions supports it in two steps:
1. Each class generates a bunch of Pointers to virtual functions in a table called virtual Table (VTBL).
2. A pointer is added to each class Object to point to the associated Virtual table. This pointer is usually called VPTR, and VPTR is set and reset automatically by each class’s constructor, destructor, and copy Assignment operators.
C++ supports polymorphism in the following ways:
1. Through a set of implicit transformation operations. For example, convert a Derived class pointer to a pointer to its public Base Type.
2. Through virtual function mechanism.
3. Via dynamic_cast and typied operators.
How much memory does the class Object require:
1. The total size of its nonstatic data members.
2. Add any hesitation alignment requirements and padding on the space.
3. Plus any additional overhead generated internally to support Virtual.
Pointer type: Tells the compiler how to interpret the memory content and its size at a particular address (for example, a string is the traditional 8 bytes (including a 4byte character pointer and an integer used to represent the length of the string). Cast is a compilation instruction that mostly does not change the actual address of a pointer. It only affects how the memory size and contents of the indicated value are interpreted.
Chapter two constructors semantics
2.1 the Default Constructor
When required by the compiler, default constructor is synthesized to perform only what the compiler needs (appropriately initializing members).
Member Class Object with Default Constructor
The compiler comes out with: If A class A contains one or more Member Class objects, then each constructor of class A must call the default constructor of each member class. The compiler expands the existing constructors, inserting code in them so that user code calls (in the same order as the member Objects declaration in class) the necessary default constructors before they are executed.
Base class with Default Constructor
The compiler calls the default constructor of all Base Class Constructors (in the order in which they are declared) before the Member Class Object’s default constructor is inserted into the call.
Class with a Virtual Function
The following two cases also require a default constructor composite:
Class declares (or inherits) a virtual function.
2. Class is derived from an inheritance chain, one or more of the Virtual Base classes.
The constructor operation occurs at compile time:
1. A virtual function table is generated by the compiler containing the addresses of the class’s virtual functions.
2. On each class object, an additional pointer member (VPTR) is synthesized by the compiler containing the address of the associated class VTBL.
A Class with a Virtual Base Class
The implementation of a Virtual Base class varies greatly from compiler to compiler. However, what each implementation has in common is the need to make the location of the Virtual Base class in each of its Derived Class Objects ready at execution time. Each constructor compiler defined for a class inserts code that allows execution access for each virtual Base class.
conclusion
In these four cases, “the compiler must synthesize a default constructor for a class that does not declare constructor.” This is only a requirement of the compiler (not the program). The reason it can do its job, This is done by “calling member Object or base class default constructor” or “initializing its virtual function mechanism or virtual Base class mechanism for each object”. A class without these four cases and without life constructor would not actually be synthesized.
In synthesized default constructor, only Base Class subobjects(child objects) and Member Class Objects are initialized. All other nonstatic data members, such as integers, Pointers to integers, arrays of integers, etc., are not initialized. These initializations are required by the program, but not by the compiler.
Beginners to C++ have two common misconceptions:
1. Any class that does not define default constructor will be synthesized.
2. The constructor constructor explicitly sets the default values for each data member in the class.
2.2 the Copy Constructor
There are three cases in which the contents of one object are used as the initial value of another Class Object.
1. The most obvious, of course, is to explicitly initialize an object.
2. When object is passed as an argument to a function
3. When a function returns a class Object.
These three cases require copy constructor.
Default Memberwise Initialization
If the class does not provide a explicit copy constructor, a class object whose initial value is “the same as another object” Internally, this is done in what is called default Memberwise initialization. That is, to copy the value of each built-in or derived data member (such as an array or pointer) from one object to another without copying its contents. For example, copy only the address of the pointer and do not copy the object to which the new pointer points. This is a shallow copy. Instead of copying member Class object, it performs a recursive memberwise initialization.
How does this recursive Memberwise initialization work?
The answer is Bitwise Copy Semantics and default Copy constructor. If the class presents Bitwise Copy Semantics, then use Bitwise Copy Semantics (the pseudo-code generated by the Bitwise Copy Semantics compiler is the memcpy function), Otherwise, the compiler generates default Copy Constructor.
When does a class not present Bitwise Copy Semantics? There are four cases:
1. When a class contains a member Class object, the member class has a default copy constructor (either declared by the class designer or synthesized by the compiler).
2. When a class inherits from a base class, the base class has the copy constructor [either explicitly declared by the class designer or synthesized by the compiler].
3. When a class declares one or more virtual functions
4. When a class is derived from an inheritance chain, one or more of the virtual Base classes
Here’s a look at why bitwise Copy isn’t available in these four cases, and what the compiler-generated copy Constructor does.
In the first two cases, the compiler must insert the member or Base class’s “call operation of Copy Constructor” into the synthesized copy constructor.
Reset the pointer to the Virtual Table
In the third case, since the class contains a virtual function, we need to expand it at compile time:
1. Add virtual function table, which contains the address of a useful virtual function;
2. Create a pointer to the virtual function table and insert it into the class Object.
Therefore, the compiler must assign the VPTR of each new class object correctly, otherwise it will run to perform the function of other objects, with serious consequences. Therefore, when the compiler imports a VPTR into a class, that class no longer represents Bitwise Semantics and must synthesize Copy Constructor and initialize VPTR appropriately.
Handles Virtual Base Class Subobject
The existence of a virtual Base class requires special handling. A class object with another virtual Base class subobject will also invalidate bitwise Copy Semantics.
Every compiler’s promise to support virtual inheritance is that the “virtual Base Class Class Subobject location in the Derived Class Object” must be ready at execution time, and that it is the compiler’s responsibility to maintain the “integrity of the location.” Bitwise Copy Semantics may corrupt this location, so the compiler must synthesize copy Constructor itself.
This means that copy constructors, like default constructors, are built when needed, rather than being built by programmers without writing a compiler.
2.4 Initializing the list
There are four cases in which you must use an initializer list to initialize a class member:
1. Initialize a reference member;
When initializing a const member;
3. When a base class constructor is called with a set of arguments (which are, in effect, custom constructors);
4. When a member class constructor is called and it has a set of arguments.
However, the order of initialization is determined by the class Members declaration order, not by the initializer list.
Chapter 3 Data semantics
3.2 Data Member layout
Nonstatic data members are placed in the same order as they are declared in a class object. However, the C++ standard allows the compiler to arrange data members in multiple access sections regardless of the order in which they appear in the class declaration.
3.3 Data Member access
Private public protected access for each member, and the association with the class, There is no additional burden on space or execution time — either in individual class objects or in static data members themselves.
Static data members are treated as global variables and have only one entity stored in the program’s data segment. Each fetch of a static member is internally converted to a direct reference to that unique extern entity. Static data member; static data member; static data member;
To access a nonstatic data member, the compiler needs to add the starting address of the class object to the data member offset (which is known at compile time).
3.4 Inheritance and Data Member
As long as inheritance is not polymorphic
This situation does not add an additional burden on space or storage time. In this case, base class and Derived class objects all start at the same address. The only difference is that the Derived Object is larger to accommodate self-built nonstatic data members. Specifying a Derived Class Object to a pointer or reference to a base class does not require the compiler to mediate or modify the address, it can happen gracefully and provides optimal execution efficiency.
Combined with polymorphic
This situation imposes an additional burden on space and access time:
1. Import a and virtual table to store the addresses of each declared virtual function.
2. Import a VPTR into each class object and provide a link during execution so that each object can find the corresponding virtual table.
3. Enhance constructor to initialize VPTR to point to the virtual table corresponding to the class.
4. Strengthen destructor so that it can erase VPTR “pointing to class-related virtual table”.
Multiple inheritance
For a multiple derived object, assigning the address to the “leftmost (first) base class pointer” is the same as for single inheritance, since both refer to the same starting address. For the second or subsequent base class, the address has been changed: Add (or subtract, if downcast) the size of the intermediate Base class subobject(s).
If you want to access a data member in the second (or subsequent) base class, there is no additional cost because members are fixed at compile time, so accessing member is a simple operation of offset.
Virtual inheritance
A class containing one or more Virtual Base class subobjects is split into two parts: an immutable part and a shared part. Data in an immutable part can always have a fixed offset, and that part can be accessed directly, while the shared part represents the Virtual Base Class subobject, which can only be accessed indirectly because its position changes with each derivation.
In the absence of virtual functions, they are exactly the same as C structs.
Chapter 4 Function Semantics
4.1 Various invocation methods of Member
Nonstatic Member Functions
The compiler internalizes the member function as nonmember, through the following conversion steps:
1. Add an extra parameter — this to the function.
2. Change the access to each nonstaitc data member to this.
Rewrite member Function as an external function. Mangling of function names makes them unique vocabularies.
Virtual Member Functions
will
ptr->f(); //f() is a virtual member function
Internal conversion to
(* PTR – > VPTR [1] (PTR);
Among them:
VPTR represents a compiler generated pointer to the Virtual table. It is embedded in every Class Object that declares (or inherits) one or more virtual functions.
1 is the index of the virtual Table slot, associated with the normalize() function.
The second PTR represents the this pointer.
Static Member Functions
Cannot be declared const volatile or virtual.
A static member function is raised outside the class declaration and given an appropriate name to pass through the mangling. Static member function (); static member function (); static member function ();
&Point::count();
Returns a value of type:
unsigned int(*)();
Instead of:
unsigned int(Point::*)();
4.2 Virtual Member Funcitons
In C++, polymorphism means “addressing a derived class object with a pointer (or reference) to a public base class.
Each class has only one virtual table, and each table contains the addresses of all active Virtual functions entities in the corresponding class Object. These active virtual functions include:
1. Overriding a possible base class virtual function entity that this class defines.
2. Function entities derived from Base Class (not overwritten by Derived Class)
3. A pure_virtual_called().
There are three possibilities for a class to inherit the function Virtual Table:
1. Inherit virtual functions declared by base Class. Specifically, the address of the function entity is copied into the slot corresponding to the Virtual table of the Derived class.
2. Use your own function entities. This means that its own function entity address must be placed in the corresponding slot.
3. You can add a new virtual function. In this case, the virtual table size will increase by a slot into the function entity address.
Set virtual function calls at compile time:
In general, I don’t know the true type of object to which a PTR refers. However, the virtual table of this object can be accessed via PTR.
Although I don’t know which Z() function entity is called, I do know that each Z() function address is placed in the index of Slot 4.
So that we can take
ptr->z();
Convert to :(* PTR -> VPTR [4])(PTR);
The only thing known at execution time is which z() function entity slot4 refers to.
Virtual Functions under multiple inheritance
Virtual functions are supported in multiple inheritance, with complexity around the second and subsequent base classes, and the fact that the this pointer must be adjusted at execution time. The general rule is to call a Derived Class virtual function via a pointer to a “second or subsequent base class.” The necessary this pointer adjustment operation associated with the call operation must be completed at execution time.
Virtual Functions under Virtual inheritance
4.3 Efficiency of functions
Nonmemeber, static member, or nonstatic member functions are all converted to exactly the same form, so all three are equally efficient.
4.4 Pointer to Member Function
Take the address of a nonstatic data member. The result is the bytes position of the member in the class layout, so it needs to be bound to the address of some class object before it can be accessed.
If the function is nonvirtual, then the memory address is the actual memory address. This value is also incomplete and must be bound to some class Object address before the function can be called.
Pointer to Virtual Member Function is supported
For a virtual function, its address is unknown at compile time, all that can be known is the index value of the virtual function in its related Virtual table, that is to say, for a virtual member function to take its address, All you can get is an index value.
4.5 the Inline Funcitons
The parameter is passed in as a parameter, and the constant is replaced directly. If the constant is passed in as a function result, the temporary variable needs to be imported
Local variables Local variables are mangling so that inline functions are replaced with unique names, which means that N temporary variables appear when called N times at once… The size of the program will explode
Chapter five: Constructing, deconstructing and copying Semantics
Object construction under inheritance architecture
What steps accompany a constructor call:
The data members of the member Initialization List are placed into the constructor function itself, in the order in which the MEMBS was declared.
2. If a member is not in the initializer list but is in a default constructor, that default constructor must be called (manually).
3. Before that, if the class Object has a Virtual Table Pointer (s), it must be initialized to specify the appropriate Virtual table(s).
4. Before that, all the base Class constructors of the previous layer must be called, in the order in which the base class was declared (independent of the order in which the initializer list was initialized).
If base Class is listed in the initializer list, any explicitly specified parameters should be passed.
If base class is not listed in the initialization list, call default constructor.
If base class is the second or subsequent base class under multiple inheritance, the this pointer must be adjusted.
5. Before that, all Virtual Base Class Constructors must be called, from left to right, from deepest to shallowest.
If class is listed in the initializer, then any explicitly specified arguments should be passed in. If not, call Default Constructor.
In addition, the offset of each Virtual Base Class Subobject in the class must be accessible at execution time.
If the class Object is the lowest class, a constructors may be called; Some mechanism to support this behavior must be put in place.
Object copy semantics
When designing a class and specifying one class Object as another class object, there are three options:
1. Do nothing and implement default behavior.
2. Provide a explicit Copy Assignment operator.
3. Explicitly reject the assignment of one class object to another.
A class does not exhibit the bitwise copy semantics for the default copy assignment operator in the following cases:
Copy Assignment Operator = copy Assignment operator = copy Assignment operator
A member object of a class has a copy Assignment operator.
3. When a class declares any virtual functions,
4. When a class inherits a Virtual Base class.
VPTR semantics.
When is VPTR initialized from constructor? After the base Class Constructors call the operation, but before the code supplied by the programmer or the members operation listed in the initializer list.
Deconstructing semantics
How destructor can be extended:
1. The destructor function itself is executed first.
2. If the class owns Member Class Objects and then destructor, they will be called in the reverse order of the declaration order.
3. If the object has a VPTR inside it, it is now reset to point to the appropriate Base Class Virtual Table.
4. If any direct nonvirtual Base classes own destructor, they will be called in reverse order of declaration.
5. If any virtual base classes have destructor, and the class in question is the last class, they will be called in reverse order.
Chapter vi Execution Semantics
Chapter 7…
Added: type upward transition and confusion of polymorphism
Construct such an inheritance system:
class Base {
public: virtual ~Base() {}
virtual void show() { cout << “Base” << endl; }
};
class Derived : public Base {
public: void show() { cout << “Derived” << endl; }
};
The subclass Derived overrides the show method in the Base class. Write the following test code:
Base b; Derived d;
b.show(); d.show();
The result is:
Base
Derived
The Base object calls the Base method, and the Derived object calls the Derived method. Because polymorphisms are not enabled when member functions are called directly from objects, the compiler determines which show function to call based on the type of b and D. In both cases, the compiler determines a unique entry address for each of the two calls. This is actually similar to an overloaded polymorphism, although the two show functions have different scopes. How about this: Base B; Derived d; b.show(); b = d; b.show(); Now, an object of Base is assigned to an object of subclass Derived.
How about this:
Base b; Derived d;
b.show(); b = d; b.show();
Now, an object of Base is assigned to an object of subclass Derived.
The result is:
Base
Base
For those familiar with Java, this is incomprehensible. But actually, C++ is not Java, it’s more like C. “B = D” does not mean “let a reference to a Base class refer to its subclass object”, but rather “divide the Base subclass object from the Base subclass object and assign it to B”. So, as long as b is always of type Base, then b. How () always calls the show function of the Base class; In other words, the compiler always treats the entry address of the show function in Base as the entry address of b.show(). It doesn’t use polymorphism at all.
Rewriting polymorphisms under single inheritance
So let’s do this again:
Base b; Derived d;
Base *p = &b;
p->show();
p = &d;
p->show();
In this case, the result is correct:
Base
Derived
The first time it points to a Base object, p->show() calls the Base show function; The second time it points to a Derived object, p->show() calls the show function of the Derived class.
Conclusion: That is, only Pointers or references are truly polymorphic. Assigning a child object to a parent object is actually a type upconversion…
What I find confusing about C++ (constantly updated) :
1. Const and pointer decorations
const char * a; // a pointer to const char
char const *a; // A is a constant and cannot be changed
char * const a; // first a is a pointer and then a const
const (char*) a; The pointer itself is a constant. The pointer itself cannot be changed
We can see that a const char(the type itself or the dereference of *variable) refers to a constant, whereas a const char refers to a constant. Const char *a; const const *a; const char *a; const char *a; const char *a; const char *a; const char *a; const char *a; const char *a; const char *a; const char *a
Const char * const a; The first const is a type constant, and the second is a pointer constant. Const char &a; const char *a; Used when passing parameters.
2. Array and pointer combination problem
char * a[M]; This is an array of Pointers, where each element is a pointer, and each element is initialized. A [M] looks like an array. Each element of this array is char *, so we can extend char * to a one-dimensional array and then a[M] becomes a two-dimensional array. It’s just M Pointers.
char (*a)[N]; This is a pointer, and this pointer to N char elements, which is a pointer to an array, is really just a pointer. Look at (*a) as a variable, and the variable is a pointer to N elements, so it’s just a one-dimensional array. Char (*a)[N] = char b[N]
Similarly, can also be used to decorate the truth to distinguish, you can experience. The two-dimensional array dynamically allocated more wonderful can refer to my another blog dsqiu.iteye.com/blog/168314…
C++ variable initialization
Local variables of built-in type are not initialized, but global variables are initialized by default when assigned addresses. Local variables of class type (not explicitly initialized) are initialized by default (there is a default constructor, otherwise an error is reported), but their built-in data members are not initialized (if not initialized in the default constructor). Same thing with arrays.
= = = = = = = =
Blog.csdn.net/charce1989/… – column