IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++【对象模型】| 【08】类在执行期会处理哪些事呢? -> 正文阅读

[C++知识库]C++【对象模型】| 【08】类在执行期会处理哪些事呢?

索引

C++【对象模型】| 【01】简单了解C++对象模型的布局
C++【对象模型】|【02】构造函数何时才会被编译器自动生成?
C++【对象模型】|【03】拷贝构造是如何工作的,何时才会用到呢?
C++【对象模型】 | 【04】程序在内部被编译器如何转化?
C++【对象模型】 | 【05】类与类之间各种关系下对数据成员的存取、绑定、布局
C++【对象模型】| 【07】构造、析构、拷贝做了哪些事?
C++【对象模型】| 【08】类在执行期会处理哪些事呢?
C++【对象模型】| 【09】类模板、异常处理及执行期类型识别

临时对象

class Y {
public:
	Y();
	~Y();
	bool operator==(const Y&) const;
};

class X{
public:
	X();
	~X();
	operator Y() const;
	X getValue();
};

void test() {
	X xx;
	Y yy;
	if(yy == xx.getValue()) {
	// yy.operator==(xx.getValue().operatorY())
	}
}
// yy.operator==(xx.getValue().operatorY())
===>上述过程将会产生三个临时对象
X temp1 = xx.getValue();	
Y temp2 = temp1.operator Y();
int temp2 = yy.operator==temp2();
===>结合if
if(temp3)....
temp2.Y::~Y();
temp1.X::~X();

对象的构造和析构

在{}内析构一般被编译器插入在离开点(如下所示);
// return离开函数前调用析构
void test(bool flag)
{
	if(flag) return -1;
	
	Point point;
	switch(int(point.x())) {
		case -1:
			// call析构
			return;
		case -1:
			// call析构
			return;
		case 1:
			// call析构
			return;
		default:
			// call析构
			return;
	}
	//call析构
}
一般obj将会在要使用的程序附近定义出,以节省非必要的对象构造和析构(如上述代码,对象在检查flag后定义定义);

全局对象

class Matrix{
public:
	Matrix();
	~Matrix();
};

Matrix identity;	// 全局对象

int main() {
	Matrix m1 = identity;
	
	return 0;
}
C++中所有的全局对象都被放置在data segment;如果没有显示指定,则被初始化为0(编译期),但其构造会在程序启动才会调用;

munch方法

早期cfront提供一个可移植(UNIX)但成本较高的静态初始化方法(munch);
它是如何工作的呢?
- 为每一个需要静态初始化的文件产生一个_sti()函数,含有构造或inline expansions;	
	__sti()[__sti__matrix_c__identity()] { identity.Matrux::Matrix();  }
- 与之对应的产生一个__std()函数,内含析构函数;
- 提供runtime library `munch`函数:一个_main()【作为main的第一个指令】调用__sti(),一个_exit()调用__std();

【有了上述方法,那么该如何收集各个对象的sti和std函数呢】
- 使用mn命令,该命令将用于可执行文件,将对象的符号表格项目导到munch中;
- 此时munch会搜索以_sti或__std开头的名称将该函数名称添加到__sti或__std函数的跳离表格中;
- 在将这个表格写到一个小的程序文本文件;
- 在将编译重新激活,将该表格的文件加以编译,整个可执行文件被重新链接;

System V1.0提供一个快速变种办法

该方法假设可执行文件是System V COFF格式,当检验该文件并找出有__link nodes并内含一个指针,指向__sti和__std函数
文件,将它串链在一起;在将链表的根源设为一个全局性的__head obj;其内含另一种不同的_main()和_exit()将以_head为起
始的链表走访一遍;

扩充链接器和目的文件格式,以求直接支持静态初始化和内存释放操作

如System V的DLF被扩充以增加.init和.fini两个section,这两个section内含对象所需要的信息,分别对于静态初始化和释放操作;

cfront2.0后支持nonclass object的静态初始化

而支持`nonclass object`的静态初始化,在某种程度上,是支持virtual base classes的一个副产品;

静态初始化的对象,有一些缺点

- 如果支持exception handing,则此类obj不能置于try中,可能无法实现静态调用构造,因为theow会触发终止函数;
- 为了控制需要跨越模块做静态初始化的对象的相依顺序的复杂度;

建议

不要用那些需要静态初始化的全局对象;

局部静态对象

此类对象的构造和析构只能执行一次,即使它所在的函数被调用多次;
编译器将会无条件起始时构造出对象,会导致没有被调用的函数内部的静态对象也被初始化;

如何才能让使用该函数时,才会将此类对象构造呢

const Matrix& identity() {
	static Matrix mat_identity;
	return mat_identity;
}
【cfont】:
先导入一个临时性对象来保护mat_identity的初始化操作;第一次处理identity()时,该临时对象被设置为false,故构造被调用
在将临时对象该为true;而析构根据是否被构造(临时对象为true)即可判断;
而由于该mat_identity是local,无法在静态的内存释放函数中存取它,其地址在downstream component中将会被转换到程序用
来放置全局对象的数据段;
static struct Matrix *__0__F3=0;
struct Martix* identity__Fv() {
	static struct Matrix __1mat_identity;
	__0__F3 ? 0 : (__ct__6MatrixFv(&__1mat_identity), (__0__F3 = (&__1mat_identity)));
}

析构在于程序文件有关的静态内存释放函数中调用:
char __std_stat_0_c_j() {
	__0__F3 ? __dt__6MatrixFv(__0__F3, 2) : 0;
}
上述指针的使用是cfront特有的,条件式析构时所有编译器都需要的;

对象数组

class Point {
public:
	Point();
	~Point();
};
Point knots[10];
在cfront中,使用vec_new()函数产生以class obj构造而成的数组,而其他较新的编译器将提供两个函数分别处理`没有虚基类
的类`和`内含虚基类的类`;
在Sun中,new出来的数使用_vector_new2(),而不是new的使用_vector_con(),他们都各自拥有一个虚基类函数实例;
void* vec_new(void *array, size_t elem_size, int elem_count, 
			void (*constructor)(void*), void(*destructor)(void*, char))
当数组Point knots[10];创建时,将会调用10次constructor作用于elem_count个对象上,当离开时,将会调用其destructor;
// 当提供初值
Point knots[10] = {Point(),-1.0};
那么获得初值的元素,不需要vec_new();
==> vec_new(&knots + 2, sizeof(Point), 8, &Point::Point, &Point::~Point);

默认构造和数组

在程序中,我们无法对构造函数取其地址,编译器中可以;
cfont2.0后才支持,有构造函数的类能生成数组;

那么cfont2.0是怎么做的呢?

根据上述代码中
Point knots[10] = {Point(),-1.0};
编译器需要这样调用
vec_new(&knots + 2, sizeof(Point), 8, &Point::Point, &Point::~Point);
cfont在内部产生一个stub construct,没有参数;用于调用程序员提供的构造,并将默认参数值限制地指定过去(但由此不能
成为内联);
Point::Point() {
	Point();
}
只有当类对象数组真正被产生时,stub实例才会被产生以及被使用;

new和delete运算符

Point *pi = new Point();
delete pi;
if(pi != 0) {	
	Point::~Point(pi);
	__delete(p1);
}
当使用delete释放一个对象,编译器会在此前做出一个判断用于保护该对象;
pi所指对象的生命会因delete而结束,故一般考虑将其继续使用;
【注意】但是pi仍然可以当作指针继续使用(仍然指向合法空间),只是存储其中的对象不在合法;
new T[0];
每次new会传回一个独一无二的指针,指向默认为1byte的内存区块

针对数组的new语意

Point* p = new Point[5];
当一个类有构造和析构时,一般vec_new()会被调用;
在C++2.0之前,程序员在使用delete数组时,需要使提供大小,由于需要针对所指的内存空间,及其个数;
而后该使用大小由编译器来处理;

编译器如何记录元素个数呢?

为vec_new()所传回的每一个内存区块配置一个额外的word,记录元素个数;
typedef void *PV;
extern int __insert_new_array(PV array_key, int elem_count);
extern int __remove_old_array(PV array_key);

PV __vec_new(PV ptr_array, int elem_count, int size, PV construct) {
	int alloc = 0;
	int array_sz = elem_count * size;
	if(alloc = ptr_array == 0) {
		ptr_array = PV(new char[array_sz]);
	}
	if(ptr_array == 0) return 0;
	int status = __insert_new_array(ptr_array, elem_count);
	if(status == -1) {
		if(alloc) delete ptr_array;
		return 0
	}
	if(construct) {
		register char* elem = (char*)ptr_array;
		register char* lim = elem + array_sz;
		register PF fp = PF(construct);
		while(elem < lim) {
			(*fp)((void*)elem);
			elem += size;
		}
	}
	return PV(ptr_array);
}

当出现虚拟机制中析构的调用并不是我们想要的

若使用基类通过指针来接收子类创建的数组,传递过去的对象大小不一致,导致执行错误的析构;
应该避免一个基类指针指向一个子类对象所组成的数组;

Placement Operator new的语意

重载定义好的new,它的第二参数而void*;该参数用来存放内存区块,放置新产生的对象;

在已存在的对象上构造新的对象,会发生什么?

若在Placement new在一个已存在的对象上构造新对象,则原对象的析构将不会被调用;如若需要,则要手动调用析构;

我们如何知道该内存区块释放需要先析构才能使用

语言层面上对此并没有做出回答,只是必须指向相同类型的class;

Placement new不支持多态

临时性对象

当使用
c = a + b;
==>
T temp;
temp.operator+(a, b);
c.operator=(temp);
temp.T::~T();

完整表达式

完整表达式的析构应时求值过程的最后一个步骤
((objA > 1024) && (objB > 1024)) ? objA + objB : foo(objA, objB);
临时对象在完整表达式尚未被评估完全之前,不得被摧毁;
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 11:40:56  更:2022-05-10 11:41:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 21:42:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码