C++中对象模型和this指针是面向对象编程中的重要概念。对象模型描述了对象在内存中的布局和行为,包括成员变量和成员函数的存储方式和访问权限。this指针是一个隐含的指针,指向当前对象的地址,用于在成员函数中引用当前对象的成员变量和成员函数。对象模型和this指针的理解和应用,对于深入理解C++的面向对象特性和实现细节至关重要。
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
class Person {
public:
Person() {
mA = 0;
}
//非静态成员变量占对象空间
int mA;
//静态成员变量不占对象空间
static int mB;
//函数也不占对象空间,所有函数共享一个函数实例
void func() {
cout << "mA:" << this->mA << endl;
}
//静态成员函数也不占对象空间
static void sfunc() {
}
};
int main() {
cout << sizeof(Person) << endl;
system("pause");
return 0;
}
上面的代码定义了一个名为Person
的类。Person
类有一个默认构造函数,用于初始化非静态成员变量mA
为0。Person
类还有一个非静态成员变量mA
和一个静态成员变量mB
。静态成员变量mB
不占用对象的空间,而是在全局数据区分配内存。Person
类还有一个成员函数func()
,用于输出非静态成员变量mA
的值。成员函数也不占用对象的空间,所有对象共享一个函数实例。Person
类还有一个静态成员函数sfunc()
,也不占用对象的空间。在main
函数中,使用sizeof
运算符输出Person
类的大小。最后调用system("pause")
暂停程序的执行,返回0表示程序正常结束。
通过前面我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:
Person(int age)
{
//1、当形参和成员变量同名时,可用this指针来区分
this->age = age;
}
Person& PersonAddPerson(Person p)
{
this->age += p.age;
//返回对象本身
return *this;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
cout << "p2.age = " << p2.age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
这段代码定义了一个Person
类,其中包含一个构造函数和一个成员函数PersonAddPerson
。test01
函数创建了两个Person
对象p1
和p2
,并测试了PersonAddPerson
函数的功能。
在构造函数中,使用了this
指针来区分形参和成员变量。this
指针指向当前对象,可以通过this->age
来访问成员变量age
。
PersonAddPerson
函数接受一个Person
对象作为参数,将该对象的age
加到当前对象的age
上,并返回当前对象的引用。
在test01
函数中,首先创建了一个age
为10的Person
对象p1,并输出其age
值。然后创建了另一个age
为10的Person
对象p2,并连续三次调用PersonAddPerson
函数,每次传入p1
作为参数。最后输出p2
的age
值。
由于PersonAddPerson
函数返回的是当前对象的引用,所以可以连续调用该函数。因此,p2
的age
值会依次增加30,最终输出为40。
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
示例:
//空指针访问成员函数
class Person {
public:
void ShowClassName() {
cout << "我是Person类!" << endl;
}
void ShowPerson() {
if (this == NULL) {
return;
}
cout << mAge << endl;
}
public:
int mAge;
};
void test01()
{
Person * p = NULL;
p->ShowClassName(); //空指针,可以调用成员函数
p->ShowPerson(); //但是如果成员函数中用到了this指针,就不可以了
}
int main() {
test01();
system("pause");
return 0;
}
这段代码定义了一个Person
类,其中包含两个成员函数ShowClassName
和ShowPerson
,以及一个成员变量mAge
。test01
函数创建了一个空指针p
,并尝试调用p
的成员函数。
在C++中,空指针是指向任何对象的指针,因此可以通过空指针调用成员函数。在ShowClassName
函数中,没有使用this
指针,所以可以正常调用,输出结果为"我是Person类!"。
但是在ShowPerson
函数中,使用了this
指针来访问成员变量mAge
。当空指针调用该函数时,this
指针为NULL
,因此访问成员变量时会出现错误。为了避免空指针访问,可以在函数体内通过判断this
是否为NULL
来提前返回,不执行后续代码。
总结:空指针可以调用成员函数,但是如果成员函数中使用了this
指针来访问成员变量,需要注意空指针的处理,避免出现错误。
示例:
class Person {
public:
Person() {
m_A = 0;
m_B = 0;
}
//this指针的本质是一个指针常量,指针的指向不可修改
//如果想让指针指向的值也不可以修改,需要声明常函数
void ShowPerson() const {
//const Type* const pointer;
//this = NULL; //不能修改指针的指向 Person* const this;
//this->mA = 100; //但是this指针指向的对象的数据是可以修改的
//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
this->m_B = 100;
}
void MyFunc() const {
//mA = 10000;
}
public:
int m_A;
mutable int m_B; //可修改 可变的
};
//const修饰对象 常对象
void test01() {
const Person person; //常量对象
cout << person.m_A << endl;
//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
person.m_B = 100; //但是常对象可以修改mutable修饰成员变量
//常对象访问成员函数
person.MyFunc(); //常对象不能调用const的函数
}
int main() {
test01();
system("pause");
return 0;
}
这段代码定义了一个名为Person
的类。Person
类有一个默认构造函数,用于初始化成员变量m_A
和m_B
为0。Person
类还有一个成员函数ShowPerson()
,使用const
修饰,表示该函数是一个常函数。常函数中的this
指针是一个指针常量,指针的指向不可修改。但是,常函数可以修改this
指针指向的对象的mutable
修饰的成员变量m_B
的值。Person
类还有一个成员函数MyFunc()
,同样使用const
修饰,但该函数尝试修改成员变量m_A
的值,因此会导致编译错误。
在main
函数中,定义了一个常量对象person
,使用const
修饰。常量对象不能修改成员变量的值,但可以访问成员变量的值。常量对象可以修改mutable
修饰的成员变量m_B
的值。常量对象也可以调用常函数ShowPerson()
,但无法调用MyFunc()
。
最后调用system("pause")
暂停程序的执行,返回0表示程序正常结束。