进程内存空间布局
- 当运行一个可执行文件时,操作系统就会把这个可执行文件加载到内存中,此时进程就会有一个虚拟的地址空间(内存空间)。
- BSS存放未初始化或者初始化为0的全局变量。
- Linux下,可以通过nm列出可执行文件中的全局变量的地址。
- 全局变量以及全局的函数在编译时就已经分配了地址了,这个地址是固定的线性地址,不管程序运行多少次,运行多少个实例,它的线性地址始终是确定的,而且是唯一的线性地址。
(线性地址通过分页机制转换成物理地址)
数据成员布局
-
普通成员的存储顺序,和在类中的定义顺序是有关的。
(类中先定义的变量,在内存中的地址是低地址) -
一字节对齐和恢复编译器的默认对齐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#pragma pack(1) struct aet { public: int i; int j; int k; char c; int l; }; #pragma pack( // do other thing |
- 成员变量的偏移值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class A { public: int i; int j; int k; char c; int l; private: int a; int b; public: void PrintAddress() { std::cout << &A::i << std::endl; std::cout << &A::j << std::endl; std::cout << &A::k << std::endl; std::cout << &A::c << std::endl; std::cout << &A::l << std::endl; std::cout << &A::a << std::endl; std::cout << &A::b << std::endl; } } |
静态类型和动态类型
- 对象定义时的类型,编译期间就确定好的。
- 运行时才决定的类型。
一般只有指针或引用才有动态类型的说法。
静态绑定和动态绑定
- 静态绑定
- 绑定的是静态类型,所对应的函数或者属性依赖于对象的静态类型,发生在编译期。比如普通的成员函数。
- 动态绑定
- 绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期。比如虚函数。
rtti
- 虚函数表往上的四个地址是rtti的地址
- 然后这个rtti的地址往下12个字节是type_info的地址
全局变量的构造和析构
- 全局变量是放在数据段中的。
- 全局对象不给初值的情况,编译器会默认把全局对象所在的内存全部清0。
- 全局变量在编译阶段就会把空间分配出来(地址是在编译期间就确定好的)。
- 全局对象在main函数执行之前就被构造完毕,在main函数执行完毕后才被析构掉。
new a()和new a
- 对于一个空类而言,是没有区别的。
- 如果类A中有成员变量,带括号的初始化会把一些和成员变量有关的内存清0。(不是对整个对象的内存清0)
new和delete
- new先调用了operator new(),里面调用了malloc。然后调用了类的构造函数。
- delete先调用了类的析构函数,然后调用了operator delete()。
内存池
原理
- 用malloc申请一大块内存,当需要分配内存的时候,就从这一大块内存中一点一点分配。
- 当这一大块内存用光了的时候,再用malloc申请一大块内存,再用于分配。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
class Aet { public: static void* operator new(size_t size); static void operator delete(void* phead); // 分配计数统计,每分配一次,累计一次 static int count_; // 没malloc一次,累计一次 static int malloc_count_; private: Aet* next; // 总是指向一块可以分配出去的内存的首地址 static Aet* free_pos_; // 一次分配多少倍的该类内存 static int trunk_count_; void func() {} }; int Aet::count_ = 0; int Aet::malloc_count_ = 0; Aet* Aet::free_pos_ = nullptr; int Aet::trunk_count_ = 5; // 一次分配5倍的该类内存地址 void* Aet::operator new(size_t size) { Aet* tmp; if (free_pos_ == nullptr) { // 申请一大块内存 size_t realsize = trunk_count_ * size; free_pos_ = reinterpret_cast<Aet*>(new char[realsize]); tmp = free_pos_; for(; tmp != &free_pos_[trunk_count_ - 1]; ++tmp) { tmp->next = tmp + 1; } tmp->next = nullptr; ++malloc_count_; } tmp = free_pos_; free_pos_ = free_pos_->next; ++count_; return tmp; } void Aet::operator delete(void * phead) { (static_cast<Aet*>(phead))->next = free_pos_; free_pos_ = static_cast<Aet*>(phead); } |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 深度探索C++对象模型:对象、构造、数据、函数、执行09/10
- ♥ C++并发编程 _ 基于锁的数据结构08/19
- ♥ CLion:配置C++下lua开发环境06/03
- ♥ 包管理器:设计与实现09/18
- ♥ Effective C++_第三篇07/01
- ♥ Objective-C 解析plist04/28