4.3 C++对象模型和this指针
4.3.1 成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
#include <iostream>
class Person {
public:
Person() {
mA = 0;
}
//非静态成员变量占对象空间
int mA;
//静态成员变量不占对象空间
static int mB;
//函数也不占对象空间,所有函数共享一个函数实例
void func() {
std::cout << "mA:" << this->mA << std::endl;
}
//静态成员函数也不占对象空间
static void sfunc() {
}
};
int main() {
//实例化一个对象为p1
Person p1;
std::cout << "Person:"<<sizeof(Person) << std::endl;
std::cout << "p1:" << sizeof(p1) << std::endl;
return 0;
}
运行结果如下:
4.3.2 this指针概念
通过4.3.1我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题。
this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针是一个常量指针,可以看做const type * this ,指针的指向不能修改,this = NULL这样是错的。
this指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this
#include <iostream>
class Person {
public:
Person(int age) {
//1 使用this区分形参和成员变量
this->age = age;
}
Person& PersonAddPerson(Person p) {
this->age += p.age;
//2 返回对象本身,this为指针,所以加上*
return *this;
}
//定义一个成员变量,属性为public
int age;
};
void test01()
{
//实例化一个对象为p1,会调用有参构造函数
Person p1(10);
//打印p1.age的结果
std::cout << "p1.age:" << p1.age << std::endl;
//实例化一个对象为p2
Person p2(12);
//p2.PersonAddPerson(p1)这是使用p2对象调用成员函数PersonAddPerson(p1),
//成员函数返回值是对象p2,然后再次调用成员函数
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
std::cout << "p2.age:" << p2.age << std::endl;
}
int main() {
test01();
return 0;
}
运行结果如下:
#include <iostream>
class MyClass {
public:
void printAddress() {
std::cout << "Address of the current object: " << this << std::endl;
}
void modifyObject() {
// 下面的语句是非法的,会导致编译错误
// this = nullptr; // Error: assignment of read-only parameter 'this'
// 修改成员变量是合法的
data = 42;
}
private:
int data;
};
int main() {
MyClass obj;
// 调用成员函数,显示对象地址
obj.printAddress();
// 调用修改对象的函数
obj.modifyObject();
return 0;
}
4.3.3 空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
示例:
#include <iostream>
class Person {
public:
void ShowClassName() {
std::cout << "Person类" << std::endl;
}
void ShowPerson() {
if(this == NULL)
return ;
std::cout << "age:" << age << std::endl;
}
int age;
};
void test01()
{
//创建一个对象指针
Person* p1 = NULL;
//使用对象指针调用成员函数
p1->ShowClassName();
//使用对象指针调用成员函数,成员函数使用了this指针,空对象指针就使用不了
p1->ShowPerson();
}
int main() {
test01();
return 0;
}
执行结果如下:
成员函数去掉this判断部分
#include <iostream>
class Person {
public:
void ShowClassName() {
std::cout << "Person类" << std::endl;
}
void ShowPerson() {
/*if(this == NULL)
return ;
*/
std::cout << "age:" << age << std::endl;
}
int age;
};
void test01()
{
//创建一个对象指针
Person* p1 = NULL;
//使用对象指针调用成员函数
p1->ShowClassName();
//使用对象指针调用成员函数,成员函数使用了this指针,空对象指针就使用不了
p1->ShowPerson();
}
int main() {
test01();
return 0;
}
再次编译执行结果如下
对于上面出现的错误做一个解释,
代码中,如果去掉了 if(this == NULL)
的检查,并试图在一个空指针上调用 ShowPerson
函数,程序会尝试通过一个空指针来访问 age
成员变量。对空指针进行解引用是一种未定义的行为,通常会导致段错误。
具体步骤如下:
- 声明一个空指针,例如
Person* nullPointer = nullptr;
。 - 尝试在这个空指针上调用一个成员函数:
nullPointer->ShowPerson();
。 - 在
ShowPerson
函数内部,尝试通过this->age
访问age
成员变量。 - 由于
this
是一个空指针,尝试访问this->age
会导致段错误。
4.3.4 const修饰成员函数
常函数:
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数
const修饰成员函数
#include <iostream>
class Person {
public:
//默认构造函数
Person() {
p_a = 0;
p_b = 0;
}
void ShowPerson() {
//隐含在每个成员函数内部都有一个this指针,this是一个常量指针
//this = NULL; 常量指针的内容不能修改
this->p_a = 10;//对于常量指针的指向的内容可以修改
}
void ShowPerson2() const {
//隐含在每个成员函数内部都有一个this指针,this是一个常量指针
//this = NULL; 常量指针的内容不能修改
//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
//this->p_a = 20;
//p_b变量被mutable修饰,可以修改
this->p_b = 100;
//常函数中变量不能修改
//p_a = 12;
//使用mutable修改的变量可以修改
p_b = 22;
}
public:
int p_a;
mutable int p_b;
};
void test01()
{
//创建一个对象,会调用默认构造函数
Person p1;
//查看变量p_a和p_b的值,调用构造函数后值都为0
std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;
//使用对象调用成员函数
p1.ShowPerson();
//查看此时变量p_a和p_b的值,p_a会变为10,p_b为0
std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;
//使用对象调用常量成员函数
p1.ShowPerson2();
//查看此时变量p_a和p_b的值,p_a会为10,p_b为22
std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;
}
int main() {
test01();
return 0;
}
运行结果如下图:
const修饰对象
#include <iostream>
class Person {
public:
//默认构造函数
Person() {
p_a = 0;
p_b = 0;
}
void ShowPerson() {
//隐含在每个成员函数内部都有一个this指针,this是一个常量指针
//this = NULL; 常量指针的内容不能修改
this->p_a = 10;//对于常量指针的指向的内容可以修改
}
void ShowPerson2() const {
//隐含在每个成员函数内部都有一个this指针,this是一个常量指针
//this = NULL; 常量指针的内容不能修改
//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
//this->p_a = 20;
//p_b变量被mutable修饰,可以修改
this->p_b = 100;
//常函数中变量不能修改
//p_a = 12;
//使用mutable修改的变量可以修改
p_b = 22;
}
public:
int p_a;
mutable int p_b;
};
void test01()
{
//创建一个常量对象p1,会调用默认构造函数
const Person p1;
//1 常量对象不能修改成员变量的值
//p1.p_a = 33;
//可以修改被mutable修饰的变量值
p1.p_b = 11;
//可以访问成员变量的值
std::cout << "p_a:" << p1.p_a << std::endl;
std::cout << "p_b:" << p1.p_b << std::endl;
//2 常量对象访问成员函数,只能访问常函数
//ShowPerson是非常函数,常量对象不能访问
//p1.ShowPerson();
//访问常函数
p1.ShowPerson2();
std::cout << "p_a:" << p1.p_a << " p_b:" << p1.p_b << std::endl;
}
int main() {
test01();
return 0;
}
运行结果如下: