一、类对象的存储方式
先说一下结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
注意空类和没有成员变量的类的大小,编译器给了这两种类一个字节来唯一标识这个类的对象。
class A
{
public:int _a;int _b;
};
class B
{// 空类
};
class C
{
public:void func(){cout << "func()" << endl;}
};
int main()
{cout << "A的大小:" << sizeof(A) << endl;cout << "B的大小:" << sizeof(B) << endl;cout << "C的大小:" << sizeof(C) << endl;return 0;
}
- 为什么成员变量存放在在类对象中,成员函数不在类对象中?
成员变量存放在类对象中,每个类对象都会拥有自己的成员变量。这些成员变量占据了每个对象的内存空间,它们具有对象的特定值和状态。当创建多个对象时,每个对象都会有自己独立的成员变量,从而保证了数据的封装性。
相比之下,成员函数并不包含对象特定的数据,它们通常只需要访问类的成员变量以及执行一些操作。因此,将成员函数与对象实例分离可以节省内存空间,避免重复存储相同的函数代码。
另外,将成员函数与对象实例分离还能提高代码的复用性。多个对象可以共享同一个类的成员函数,而无需为每个对象都存储一份相同的函数代码。这样可以减少内存消耗,并且方便维护和修改代码。
总结起来,将成员变量存放在类对象中,而将成员函数与对象实例分离,能够节省内存空间、提高代码复用性,并符合面向对象编程的封装性原则。
补充:内存对齐的规则
1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8。
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
二、this指针
- 既然内存中不存放成员函数,那么程序又是怎么确定是哪个对象调用的函数呢?
class Date
{
private:int _year;int _month;int _day;
public:Date(int year = 2024, int month = 1, int day = 14){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
};int main()
{Date d1(2023, 12, 12);Date d2;d1.Print();d2.Print();return 0;
}
在C++中,每个成员函数都有一个隐含的额外参数,即指向调用该函数的对象的指针(this指针)。通过this指针,程序能够确定是哪个对象调用了该函数。
当调用一个成员函数时,编译器会自动将调用该函数的对象的地址作为this指针传递给函数。这样,在函数内部就可以通过this指针来访问对象的成员变量和其他成员函数。
所以上面代码就会被编译器处理成这样:
void Print()
{cout << _year << "/" << _month << "/" << _day << endl;
}void Print(Date* this)
{cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
}d1.Print();
d2.Print();d1.Print(&d1);
d2.Print(&d2);
-
this指针的特性
1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
总结:
1.this指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向正在被该成员函数操作的那个对象。(全局函数没有this指针)
2.当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,由隐含使用this指针。
3.当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。
4.this指针被隐含地声明为:Class Name* const this,这意味着不能给this指针赋值;
在Class Name 类的const成员函数中,this指针的类型为:const className * const,这说明对this指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);
5.this并不是一个常规变量,而是个右值,所以不能取得this的地址(不能&this)。
6.在以下场景中,经常需要显式引用this指针:
ⅰ.为实现对象的链式引用;
ⅱ.为避免对同一对象进行赋值操作
iⅱ.在实现一些数据结构时,如list。
-
this相关题目
1、下面描述错误的是( D )
A.this指针是非静态成员函数的隐含形参.
B.每个非静态的成员函数都有一个this指针.
C.this指针是存在对象里面的.
D.this指针可以为空
在C++中,this指针始终指向当前对象的地址,它是非静态成员函数的隐含形参。this指针不可能为空,因为它指向调用该成员函数的对象的地址。在非静态成员函数中,this指针可以用于访问该对象的成员变量和成员函数。
2、下列有关this指针使用方法的叙述正确的是( D )
A.保证基类保护成员在子类中可以被访问
B.保证基类私有成员在子类中可以被访问
C.保证基类公有成员在子类中可以被访问
D.保证每个对象拥有自己的数据成员,但共享处理这些数据的代码
this指针并不影响对基类成员的访问权限,它仅仅指向当前对象,无论是基类还是子类的成员。保护成员只能在派生类内部或友元函数中访问,私有成员只能在类内部访问,而公有成员可以在任何地方访问。this指针不会改变这些访问权限。
"共享处理这些数据的代码"指的是非静态成员函数中对对象的数据进行处理的代码段,这段代码可以被多个对象共享使用。换句话说,通过this指针,每个对象可以调用同一个非静态成员函数来处理自己的数据成员,这样就避免了为每个对象都复制一份相同的代码。
例如,假设有一个类
Person
,其中包含一个非静态成员函数printName()
来打印该对象的姓名。当创建多个Person
对象时,这些对象共享同一个printName()
函数的代码,但通过各自的this指针,每个对象可以将自己的姓名传递给该函数进行打印,从而实现了数据的共享处理。
3、在C++函数中,有些函数是没有this指针的:
- 全局函数不具备this指针
- static函数不具备this指针
- 友元函数不具备this指针
空指针问题
判断:下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
1、
class A
{
public:void Print(){cout << "Print()" << endl;}
private: int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
在执行 p->Print(); 这一语句时,其实执行的是A::Print(),因为p是一个类A的指针,所以编译器会在类A中查找是否存在Print()函数,发现类中存在这个函数则编译成功。
之后就进入运行阶段,虽然this指针为nullptr,但是Print()函数中没有执行对this解引用的操作,所以可以运行成功。
2、
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA(); return 0;
}
同样的执行A::Print(),执行函数,在函数内部存在对this指针解引用的操作 this->_a ,所以运行崩溃。
今天的分享就到这里了,如果,你感觉这篇博客对你有帮助的话,就点个赞吧!感谢感谢……