adding info about memory size for virtual functions
This commit is contained in:
committed by
JamesFlare1212
parent
63ddc1cc22
commit
8102db4dac
@@ -206,3 +206,158 @@ int main() {
|
|||||||
## 26.7 Exercise
|
## 26.7 Exercise
|
||||||
|
|
||||||
What is the output of the following [program](virtual.cpp)?
|
What is the output of the following [program](virtual.cpp)?
|
||||||
|
|
||||||
|
## 26.8 Memory Usage of Virtual Functions
|
||||||
|
|
||||||
|
Given the following [program](virtual2.cpp), what is the memory size of each class?
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class Human {
|
||||||
|
};
|
||||||
|
|
||||||
|
class Student {
|
||||||
|
int age;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CollegeStudent {
|
||||||
|
int age;
|
||||||
|
void print(){
|
||||||
|
std::cout << "I am a college student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSStudent {
|
||||||
|
int age;
|
||||||
|
virtual void print(){
|
||||||
|
std::cout << "I am a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
std::cout << "memory size of Human class is: " << sizeof(Human) << std::endl;
|
||||||
|
std::cout << "memory size of Student class is: " << sizeof(Student) << std::endl;
|
||||||
|
std::cout << "memory size of College Student class is: " << sizeof(CollegeStudent) << std::endl;
|
||||||
|
std::cout << "memory size of CS Student class is: " << sizeof(CSStudent) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 26.8.1 Empty Class
|
||||||
|
|
||||||
|
- An empty C++ class takes one byte because the C++ standard requires that each distinct object has a unique address in memory.
|
||||||
|
|
||||||
|
- If an empty class had size 0, then multiple instances of that class could end up having the same memory address, which would break basic assumptions in C++ like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Empty {};
|
||||||
|
|
||||||
|
Empty a, b;
|
||||||
|
std::cout << (&a == &b); // This should be false!
|
||||||
|
```
|
||||||
|
|
||||||
|
- To make sure that &a != &b, the compiler gives each object at least one byte of storage, even if the class doesn’t contain any data.
|
||||||
|
|
||||||
|
### 26.8.2 Class CSStudent: Total size breakdown
|
||||||
|
|
||||||
|
- int age
|
||||||
|
|
||||||
|
Size: 4 bytes
|
||||||
|
|
||||||
|
- Virtual function (print)
|
||||||
|
|
||||||
|
- This makes the class polymorphic, so the compiler adds a vptr (virtual table pointer, also know as vtable pointer).
|
||||||
|
|
||||||
|
- On a 64-bit machine, a pointer is 8 bytes.
|
||||||
|
|
||||||
|
- Padding/alignment
|
||||||
|
|
||||||
|
- The compiler aligns data to certain boundaries for performance.
|
||||||
|
|
||||||
|
- Typical alignment for a class with a pointer is 8 bytes, so the 4-byte int is padded with 4 extra bytes.
|
||||||
|
|
||||||
|
### 26.8.3 Static Dispatch vs Dynamic Dispatch
|
||||||
|
|
||||||
|
- When you call a non-virtual member function like print() in the CollegeStudent class, the compiler resolves the call at compile time. This is known as static dispatch or early binding.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
CollegeStudent alice;
|
||||||
|
alice.print();
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's what happens under the hood:
|
||||||
|
|
||||||
|
- At compile time, the compiler sees that alice is of type CollegeStudent.
|
||||||
|
|
||||||
|
- It knows the exact location of CollegeStudent::print() in the compiled binary (it's in the .text segment).
|
||||||
|
|
||||||
|
- So it generates a direct call to that specific memory address. Like *call 0x123456* where *0x123456* is the address of the print() function.
|
||||||
|
|
||||||
|
- This is why the object doesn’t need to store any pointer to the function — the compiler already knows which function to call!
|
||||||
|
|
||||||
|
- If print() were marked virtual, like in the CSStudent class, then the call would become runtime-resolved using a vtable. Then:
|
||||||
|
|
||||||
|
- The object now gets a hidden pointer to a vtable (a lookup table of function pointers).
|
||||||
|
|
||||||
|
- When you call print(), the program:
|
||||||
|
|
||||||
|
- Looks up the function pointer in the vtable.
|
||||||
|
|
||||||
|
- Calls the function via that pointer.
|
||||||
|
|
||||||
|
- This is called dynamic dispatch or late binding.
|
||||||
|
|
||||||
|
Question: What if class CSStudent is defined as this, what would be the memory size of a CSStudent object?
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class CSStudent {
|
||||||
|
int age;
|
||||||
|
virtual void print(){
|
||||||
|
std::cout << "I am a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
virtual void print2(){
|
||||||
|
std::cout << "I am still a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
virtual void print3(){
|
||||||
|
std::cout << "I am still a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
To understand the problem, compile this [program](virtual3.cpp), and use the tool *pahole* to examine the memory information.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ g++ -g virtual3.cpp
|
||||||
|
$ pahole a.out
|
||||||
|
class CSStudent {
|
||||||
|
public:
|
||||||
|
|
||||||
|
void ~CSStudent(class CSStudent *, int);
|
||||||
|
|
||||||
|
void CSStudent(class CSStudent *, );
|
||||||
|
|
||||||
|
void CSStudent(class CSStudent *, const class CSStudent &);
|
||||||
|
|
||||||
|
void CSStudent(class CSStudent *);
|
||||||
|
|
||||||
|
int ()(void) * * _vptr.CSStudent; /* 0 8 */
|
||||||
|
int age; /* 8 4 */
|
||||||
|
virtual void print(class CSStudent *);
|
||||||
|
|
||||||
|
virtual void print2(class CSStudent *);
|
||||||
|
|
||||||
|
virtual void print3(class CSStudent *);
|
||||||
|
|
||||||
|
/* vtable has 3 entries: {
|
||||||
|
[0] = print((null)),
|
||||||
|
[1] = print2((null)),
|
||||||
|
[2] = print3((null)),
|
||||||
|
} */
|
||||||
|
/* size: 16, cachelines: 1, members: 2 */
|
||||||
|
/* padding: 4 */
|
||||||
|
/* last cacheline: 16 bytes */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The comment /* 0 8 */ means that the virtual table pointer starts at offset 0 of the class, and it has 8 bytes; the comment /* 8 4 */ means the variable age starts at offset 8 and it has 4 bytes.
|
||||||
|
|||||||
31
lectures/26_inheritance_II/virtual2.cpp
Normal file
31
lectures/26_inheritance_II/virtual2.cpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class Human {
|
||||||
|
};
|
||||||
|
|
||||||
|
class Student {
|
||||||
|
int age;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CollegeStudent {
|
||||||
|
int age;
|
||||||
|
void print(){
|
||||||
|
std::cout << "I am a college student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSStudent {
|
||||||
|
int age;
|
||||||
|
virtual void print(){
|
||||||
|
std::cout << "I am a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
CSStudent cs;
|
||||||
|
std::cout << "memory size of Human class is: " << sizeof(Human) << std::endl;
|
||||||
|
std::cout << "memory size of Student class is: " << sizeof(Student) << std::endl;
|
||||||
|
std::cout << "memory size of College Student class is: " << sizeof(CollegeStudent) << std::endl;
|
||||||
|
std::cout << "memory size of CS Student class is: " << sizeof(CSStudent) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
37
lectures/26_inheritance_II/virtual3.cpp
Normal file
37
lectures/26_inheritance_II/virtual3.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class Human {
|
||||||
|
};
|
||||||
|
|
||||||
|
class Student {
|
||||||
|
int age;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CollegeStudent {
|
||||||
|
int age;
|
||||||
|
void print(){
|
||||||
|
std::cout << "I am a college student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSStudent {
|
||||||
|
int age;
|
||||||
|
virtual void print(){
|
||||||
|
std::cout << "I am a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
virtual void print2(){
|
||||||
|
std::cout << "I am still a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
virtual void print3(){
|
||||||
|
std::cout << "I am still a CS student." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
CSStudent cs;
|
||||||
|
std::cout << "memory size of Human class is: " << sizeof(Human) << std::endl;
|
||||||
|
std::cout << "memory size of Student class is: " << sizeof(Student) << std::endl;
|
||||||
|
std::cout << "memory size of College Student class is: " << sizeof(CollegeStudent) << std::endl;
|
||||||
|
std::cout << "memory size of CS Student class is: " << sizeof(CSStudent) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user