类和对象

109 阅读3分钟

类的大小计算

计算类的大小的时候,也要考虑内存对齐。

class Date
{
public:
	void init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void func()
	{
		cout << "func()" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1;
	d1.init(2024, 2, 17);

	Date d2;
	d2.init(2021, 2, 17);

	// 可以看出,算出的类型仅仅包括成员变量,而不包含成员函数
	cout << sizeof(d1) << endl;  // 12
    
	return 0;
}
  • 那么为什么成员变量在对象中,而成员函数不在对象中呢?

image-20240217194003769

image-20240217152817376

有上面我们可以看出:每个对象的成员变量是不一样的(因为地址不一样,d1的地址是0x0000004a70affd18, d2的地址是0x0000004a70affd48),==而每个对象调用的成员函数是一样的==(d1d2对象调用函数有一样的地址),并且把成员函数放在了公共区域(代码段)。

结构体内存对齐

  1. 第一个成员在与结构体偏移量为0的地址处。

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

    注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。

    VS中默认对齐数是8

  3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对其参数取最小)的整数倍。

  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是

    所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

特殊类的大小

// 无成员变量的类
class A
{
public:
	void fun(){}
};

// 空类
class B
{};

int main()
{
	A a;
	B b;
	cout << sizeof(a) << endl;  // 1
	cout << sizeof(b) << endl;  // 1
	return 0;
}

没有成员变量的类大小都是一个字节。这1byte不存储有效数据只是占位,标识对象被实例化定义出来了。

this指针

引出this指针

class Date
{
public:
	void init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1;
	Date d2;

	// d1和d2都调用了同样的函数,在外面知道是d1还是d2调用。
	// 问题:在init函数内部怎么知道2023.10.1是d1的还是d2的年月日呢?
	d1.init(2023, 10, 1);
	d2.init(2023, 10, 1);
	return 0;
}

void init(int year, int month, int day)在编译阶段会被编译器处理成void init(Date* this, int year, int month, int day),这一步只能编译器处理,你不能处理。如下:

class Date
{
public:
	void init(Date* this, int year, int month, int day)
	{
        // this->也是编译器处理
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
};

// 调用的过程编译器也会自己处理
int main()
{
	Date d1;
	Date d2;
    
    // &d1不能自己写,这是编译器处理的。
	d1.init(&d1, 2023, 10, 1);
	d2.init(&d2, 2023, 10, 1);
	return 0;
}

this指针存在哪里?

​ 首先不存储在对象中。因为计算对象大小的时候,并没有计算this的大小。

​ 是存储在栈上,因为它是形参,一个隐含的形参。参数都是函数调用时push到栈上的。