11.3 C++对象模型和this指针
11.3.1 成员变量和成员函数分开储存
1.空对象占1Byte空间,用于区分不同对象所在地址的不同
class Person { // 空类
};
void test01() {
Person p1; // 空对象
cout << sizeof(p1) << endl; // 结果为1
}
2.非静态成员变量 属于 类的实例化对象中的一部分
3.静态成员变量 不属于 类的实例化对象中的一部分
4.非静态成员函数 不属于 类的实例化对象中的一部分
5.静态成员函数 不属于 类的实例化对象中的一部分
class Person {
public:
int m_age; // 属于Person类的实例化对象p1中 占4Byte
static string m_name; // 不属于Person类的实例化对象p1中 占0Byte
int getAge() { // 不属于Person类的实例化对象p1中 占0Byte
}
static string getName() { // 不属于Person类的实例化对象p1中 占0Byte
}
};
void test01() {
Person p1; // 空对象
cout << sizeof(p1) << endl; // 结果为4
}
11.3.2 this指针
this指针隐含在每一个非静态成员函数中,指向 调用该成员函数的 对象
this指针的本质:指针常量,其指向不可修改
用途1:解决形参与成员变量名冲突的问题,this指向调用成员函数的对象
class Person {
public:
Person(int age) { // 形参age与成员变量age冲突
// age = age; // 此处编译器认为是:传入的age=传入的age,导致输出脏值
this -> age = age; // 此处this会指向调用该函数的对象,即p1,此时该行代码等价于:p1.age = age,完成了对成员变量的赋值
}
int age;
};
void test() {
Person p1(18);
cout << p1.age << endl;
}
用途2:返回对象本身用*this
/*想要实现的功能:对象p1可连续调用PersonAddAge函数,不断将对象p2的age值累加到自身的age属性中*/
class Person {
public:
Person(int age) { // 构造函数,初始化age属性值
this -> age = age;
}
int age;
Person& PersonAddAge(Person p) { // 作用:调用后使本对象的age变量与传入的p对象的age变量相加
this->age += p.age;
return *this;
}
};
void test() {
Person p1(18) ,p2(20);
p1.PersonAddAge(p2).PersonAddAge(p2); // 链式编程
cout << p1.age << endl;
}
下面对关键的PersonAddAge()函数进行解释
(1)为什么返回值不是 return this->age ?
因为this->age的含义是对象自身的age属性,是个int类型的值,而在第17行想要实现的效果是p1.PersonAddAge(p2)后p1.PersonAddAge(p2)可作为p1本身再次调用PersonAddAge()函数,即p1.PersonAddAge(p2).PersonAddAge(p2)。那么返回值应该是对象本身,this作为指向对象的指针,*this自然代表对象本身,在本文中即为p1
(2)为什么函数返回类型不是Person?
若返回类型设置为Person,此时为值返回,那么系统会在return *this时调用拷贝构造函数,创建一个临时对象去接收返回值,类似于Person Temp = p1.PersonAddAge(p2),这样继续进行累加时,会产生Temp.PersonAddAge(p2)的效果,修改的是临时对象的age属性,且无法确定Temp对象的位置。经过代码验证,在返回类型为Person时,p1.PersonAddAge(p2).PersonAddAge(p2)的结果为38,即只有第一次调用PersonAddAge(p2)有效果,第二次调用将p2.age加到了Temp对象上,虽然代码不报错但是结果不正确。
所以需要设置返回类型为Person&,此时为引用返回,返回的是*this的引用,那么连续调用PersonAddAge()函数都可以正常修改p1的age属性值
11.3.3 空指针访问成员函数
C++中空指针可以调用成员函数,但是要注意是否使用了this指针,若用到this指针,则需要加判断语句保证代码的健壮性
class Person {
public:
void showAge() {
cout << "Hello World" <<endl; // 这句可以正常运行,因为只是打印一段现成的字符串
cout << m_age << endl; // 这句无法正常运行,因为m_age前会默认加this指针
}
int m_age;
};
void test() {
Person* p1 = NULL;
p1->showAge();
}
在C++中,成员函数中对属性的操作都会在属性前默认添加this指针,例如cout << m_age << endl; 实际上是 cout << this->m_age << endl;,test函数中对实例化对象的指针赋空,调用成员函数showAge()时this指针指向空地址,自然无法探知其中的属性值。可在成员函数中添加if(this==NULL) return 0;规避风险。
11.3.4 const修饰成员函数
常函数:
-
成员函数后加
const,称该函数为常函数 -
常函数内不可修改成员属性
-
成员属性声明时加关键字
mutable后,在常函数中可被修改class Person { public: void changeClass() const{ // 常函数 classname = "People"; // 修改的是带关键字“mutable”的成员属性 } mutable string classname; }; void test() { Person p1; p1.classname = "Person"; p1.changeClass(); }实质:
- 未加const时:相当于
Person* const this,此时const修饰this指针,使其指向的地址不能发生改变,即this的值不能变 - 添加const时:相当于
const Person* const this,在上述的基础上,新增的const修饰this指针指向的空间,使得this指向的地址上的数值不能变
- 未加const时:相当于
常对象:
-
声明对象前加
const,称该对象为常对象 -
常对象除被mutable修饰的属性之外,都不允许被修改
-
常对象只能调用常函数
class Person { public: void changeClass() const{ // 常函数 classname = "People"; } mutable string classname; // 被修饰的属性 }; void test() { const Person p1; // 常对象 p1.classname = "Person"; // 只 可修改被修饰的属性 p1.changeClass(); // 只可调用常函数 }

被折叠的 条评论
为什么被折叠?



