类和对象-C++对象模型和this指针
- 15.1 成员变量和成员函数分开存储
- 15.2 this指针概念
- 15.3 空指针访问成员函数
- 15.4 const修饰成员函数
15.1 成员变量和成员函数分开存储
在C++中:
- 类内的成员变量和成员函数分开存储。
- 只有非静态成员变量才属于类的对象上。
- 类对象的存储方式为字节对齐,总大小为类中最宽基本类型成员大小×非静态成员变量数量。
示例:
#include using namespace std; //成员变量和成员函数的分开 class Person {}; class Person_2 { int m_A; //非静态成员变量属于类的对象上 static string m_Name; //静态成员变量不属于类的对象上 void func() {} //非静态函数函数不属于类的对象上 static void func2() {} //静态函数函数不属于类的对象上 //只有非静态成员变量属于类的对象上 }; class Person_3 { int m_A; double m_B; char m_C; }; string m_Name = "zzz"; void test1_01() { Person p; //空对象占用内存空间为:1 //因为编译器会为每个空对象分配一个字节空间,仅仅用来区分不同空对象,使得不同空对象都有一个独一无二的内存位置。 cout << "size of p = " << sizeof(p) << endl; } void test1_02() { Person_2 p; //非静态成员变量属于类的对象上,这里有有一个int,所以是4 cout << "size of p = " << sizeof(p) << endl; } void test1_03() { Person_3 p; //字节对齐,和最大的对齐,double为8,所以为3*8 cout << "size of p = " << sizeof(p) << endl; } int main() { test1_01(); test1_02(); test1_03(); system("pause"); return 0; }
15.2 this指针概念
我们知道在C++中成员变量和成员函数是分开存储的,
每一个非静态成员函数只会诞生一份函数实例, 也就是说多个同类型的对象(一个实例化的多个对象)会共用一块函数代码。
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
C++通过提供特殊的对象指针,this指针,解决上述问题——this指针指向被调用的成员函数所属的对象。
this指针是隐含每一个非静态成员函数内的一种指针,
this指针不需要定义,直接使用即可。
this指针的用途:
- 当形参和成员变星同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用
return *this
(*this就是解引用,将这个指针解引用,就得到对象本身)。注意要指明返回数据类型为这个类的引用的数据类型。
示例:
#include using namespace std; class Person2 { public: Person2(int age) { age = age; } int age; }; class Person2_02 { public: Person2_02(int age) { this->age = age; } void PersonAddAge(Person2_02& p) { this->age += p.age; } Person2_02 PersonAddAge2(Person2_02& p) //因为返回类型是一个对象类型,如果直接用这个类的类型接收,会开辟新的内存来接收返回值 { this->age += p.age; cout << typeid(*this).name() << endl; //可以看到(*this)的数据类型是class Person2_02 return *this; } Person2_02& PersonAddAge3(Person2_02& p) //因为返回类型是一个对象类型,用引用接收,可以将本体返回。 { this->age += p.age; return *this; } int age; }; void test2_01() { Person2 p(5); cout << "p的年龄为:" << p.age << endl; Person2_02 p1(5); cout << "p1的年龄为:" << p1.age << endl; Person2_02 p2(10); cout << "p2的年龄为:" << p2.age << endl; p2.PersonAddAge(p1); cout << "改变后p2的年龄为:" << p2.age << endl; //如果我想多次调用p2的函数,那么需要每次将对象返回。 //链式编程思想 p2.PersonAddAge2(p1).PersonAddAge2(p1).PersonAddAge2(p1); //理论上应该是15+5+5+5 = 30,但是结果是15+5=20 //因为每次都是另开辟内存来接收返回的对象,导致不能在原对象上做运算。 cout << "再后来改变后p2的年龄为:" << p2.age << endl; p2.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1);//20+5+5+5 cout << "最后改变后p2的年龄为:" << p2.age << endl; } int main() { test2_01(); system("pause"); return 0; }
15.3 空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针 。
如果用到this指针,需要加以判断保证代码的健壮性。
#include using namespace std; class Person3 { public: void showClassName() { cout << "this is Person3 class" << endl; } void showPerson3Age() { if (this == NULL) { return; } //报错原因是因为传入的指针为NULL, cout << "age = " << m_Age << endl; //调用m_Age时,其实是这个代码:this->m_Age,现在this为空,所以报错。 } int m_Age; }; void test3_01() { Person3* p = NULL; p->showClassName(); p->showPerson3Age(); } int main() { test3_01(); system("pause"); return 0; }
15.4 const修饰成员函数
常函数:
- 成员函数后加const后我们称为这个函数为常函数。
- 常函数内不可以修改成员属性。
- 成员属性声明时加关键字
mutable
后,在常函数中依然可以修改。
语法:
//常函数 返回值类型 函数名() const {} //mutable mutable 数据类型 变量名;
常对象:
- 常对象的所有成员变量都不可修改。
- 成员属性声明时加关键字
mutable
后,依然可以修改。 - 声明对象前加
const
称该对象为常对象。 - 常对象只能调用常函数。
//常对象 const 对象名 对象;
示例:
#include using namespace std; class Person4 { public: //this指针本质就是一个指针常量(指针的指向不可更改) 类似于Person* const this; //加上const后,就相当于const Person *const this,即指针指向的值也不可修改。 void showPerson4() const { //this = NULL; this指针是不可修改的, //this->m_A = 100; this->m_B = 200; } void func() { m_A = 100; } int m_A; mutable int m_B; //变量加上关键字mutable后,就仍旧是可以更改的。 }; void test4_01() { Person4 p; p.showPerson4(); } void test4_02() { const Person4 p; //变为常对象 //p.m_A = 100; p.m_B = 200; //m_B加了mutable,在常对象下也是可以修改的。 //常对象只能调用常函数 p.showPerson4(); //p.func();//非常函数不可调用 //因为非常函数内可以修改成员变量,但是常对象已经被规定它的成员变量是不可修改的,所以会报错。 } int main() { test4_01(); system("pause"); return 0; }