4.3.1 成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储(虽然封装在一起,但是分开存储)
只有非静态成员变量才属于类的对象上,静态成员(包括静态成员变量和静态成员函数)和非静态成员函数不属于类的对象上。
class Person {
public:
Person() {
mA = 0;
}
//非静态成员变量占对象空间(是属于类的对象上的,当类中只有一个int时,类就只占4个字节)
int mA;
//静态成员变量不占对象空间(不属于类的对象上,在类中不占用空间)
static int mB;
//非静态成员函数也不占对象空间,所有函数共享一个函数实例(不属于类的对象上)
void func() {
cout << "mA:" << this->mA << endl;
}
//静态成员函数也不占对象空间
static void sfunc() {
}
};
int Person::mB = 10; //类内声明,类外初始化
int main() {
//空对象(对象内为空)占用的内存空间为1个字节(用于区分两个对象,两个对象不能用一个空间)
//C++编译器会为每一个对象也分配一个字节空间,是为了区分空对象占内存的位置
cout << sizeof(Person) << endl; // 调用sizeof可以访问类的大小
system("pause");
return 0;
}
4.3.2 this指针概念
问题:
C++中成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实例(所以对象都在调用这一个函数),也就是多个同类型的对象会共用一块代码,那这一块代码是如何区分那个对象调用自己的呢?
解决方案:
c++通过提供特殊的对象指针,this指针,解决上述问题。
特点:this指针指向被调用的成员函数所属的对象
隐含在每一个非静态成员函数内
不需要定义,直接使用即可
用途:
-
解决名称冲突:当形参和成员变量同名时,可用this指针来区分
如下图所示,红色框和蓝色框中各属于一份数据,用this指针进行区分,其中蓝色框为传入的形参,红色框为类中的成员变量。
-
返回对象本身:在类的非静态成员函数中返回对象本身,可使用return *this(通过解引用解开该对象)
传入的是一个类,用引用的方式返回
Person& PersonAddPerson(Person p)
链式编程示例:
其中,p2.PersonAddPerson(p1)执行后返回p2(在语法上p2.PersonAddPerson(p1) =p2),返回p2后又嵌套后面的.PersonAddPerson(p1)组合成p2.PersonAddPerson(p1),执行后再返回p2,以此类推不断嵌套;
传入的是一个类,用值的方式返回
Person PersonAddPerson1(Person p)
{
this->age += p.age;
return *this;
}
注:用值的方式返回,在执行完成员函数后会创建新的对象(属性和原对象一模一样,但是已经不是原对象),再在新对象上进行操作。
上图的执行逻辑:
首先,执行p5.PersonAddPerson(p4):
先执行成员函数(只是执行到this->age += p.age,还没有到return语句),原来的p5对象上的age会加上p4对象上的age(此时对象p5上的age是p5.age+p4.age);
再执行return语句,会创建出和p5一模一样的对象(对象上的属性一致)p5',再将p5'进行返回(此时返回的已经不是原来的p5,后续的操作都和p5没有关系);
其次,执行p5'.PersonAddPerson(p3):
先执行成员函数,p5'上的age加上p3的age;
再执行return语句,创建p5''返回;
然后,执行p5''.PersonAddPerson(p2):
先执行成员函数,p5''上的age加上p2的age;
再执行return语句,创建p5'''返回;
......
可以无线嵌套,但是已经和原始的p5没有关系,原始的p5只会加上p4的age而已,输出的p5.age的结果只会是原始的p5.age+p4.age
class Person
{
public:
Person(int age)
{
//1、当形参和成员变量同名时,可用this指针来区分
//this指针指向被调用的成员函数所属的对象
this->age = age;
}
//2、返回对象本身
Person& PersonAddPerson(Person p) //传入的是一个类,以引用的方式进行返回
{
this->age += p.age; //自身的年龄加上传入的年龄
//返回对象本身
return *this; //当调用p2时,this是指向p2的指针,如果返回指针的解引用就是p2的本体
//this是指向p2的指针,*this指向p2的本体
}
Person PersonAddPerson1(Person p) //返回值,会创建新的对象,再在新的对象上操作
{
this->age += p.age; //自身的年龄加上传入的年龄
//返回对象本身
return *this; //当调用p2时,this是指向p2的指针,如果返回指针的解引用就是p2的本体
//this是指向p2的指针,*this指向p2的本体
}
int age;
};
void test01()
{
Person p1(10);
//此时在调用p1(被调用的),所以this指针指向的是p1(被调用的成员函数)
cout << "p1.age = " << p1.age << endl;
Person p2(10);
//如果函数执行完之后能返回函数的本身,就可以进行一个嵌套
//即p2.PersonAddPerson(p1)执行后返回p2(p2.PersonAddPerson(p1) 相当于p2)
//链式编程思想,可以无限的往后延申(同cout无线延申输出)
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
//把p1的age多加几次
cout << "p2.age = " << p2.age << endl;
Person p3(1);
Person p4(2);
Person p5(3);
p5.PersonAddPerson1(p4).PersonAddPerson1(p3).PersonAddPerson1(p2);
//每次执行会复制出一份一摸一样的,但是已经不是它本身
cout << "p5.age = " << p5.age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
4.3.3 空指针访问成员函数
C++允许空指针调用成员函数(只适用于成员函数中没有用到this指针)
如果用到this指针,需要加以判断保证代码的健壮性(判断指针为空则返回)
//空指针访问成员函数
class Person {
public:
void ShowClassName() {
cout << "Person类!" << endl;
}
void ShowPerson() {
if (this == NULL) {
return;
} //空指针直接返回就不会出错,提高健壮性(防止别人传空指针搞破坏)
cout << mAge << endl;
//实际上的表达是 cout << this-> mAge << endl 其中this本身为空,不可以访问里面的值
}
public:
int mAge;
};
void test01()
{
Person * p = NULL;
p->ShowClassName(); //空指针,可以调用成员函数(只适用于成员函数中没有用到this指针)
p->ShowPerson(); //但是如果成员函数中用到了this指针,就不可以了
}
int main() {
test01();
system("pause");
return 0;
}
4.3.4 const修饰成员函数
常函数 | 常对象 | |||
定义 | 成员函数后加const | 声明对象前加const | ||
语法 | void 成员函数名() const {} | const 类名 对象名 | ||
特点 | 常函数内不可以修改成员属性(指针指向和指针指向的值) | 常对象只能调用常函数 | ||
成员变量 | 普通成员变量 | 可访问不可修改 | ||
mutable修饰的成员变量 | 可访问可修改 |
const修饰成员函数的区别见下表所示:
const修饰 | 不用const修饰 | |
语法 | void 成员函数名() const {} | void 成员函数名() {} |
类似指针语法 | const 类名 * const this | 类名 * const this |
const即修饰类,又修饰指针 | const只修饰指针 | |
this指针的指向 | 不可修改 | |
this指针指向的值 | 不可修改 | 可修改 |
class Person {
public:
Person() {
m_A = 10;
m_B = 20;
}
//如果想让指针指向的值也不可以修改,需要声明常函数
void ShowPerson() const{
//this指针(隐含在每一个非静态成员函数内)的本质是一个指针常量,指针的指向不可修改
//this == NULL; //报错,调用是this指针已经指向p,不可以再修改指向(NULL)
//普通成员函数可访问不可修改
//成员函数不加const:Person * const this 指针指向不可修改指向的值可以修改
//m_A = 100; //声明前面不加const则this指向的值可以修改
//this->m_A = 100; //this指针隐含在每一个非静态成员函数内,逻辑上同上一句
//成员函数加const:const Person * const this 指针指向和指向的值都不可以修改
//m_A = 100; //this指针指向的对象的数据是补可以修改的
//this->m_A = 100; //同上
cout << this->m_A << endl;
//mutable修饰的变量可修改可访问
cout << this->m_B << endl;
this->m_B = 100;
cout << this->m_B << endl;
cout << "访问常函数" << endl;
}
void MyFunc() {
m_A = 100;
}
public:
int m_A ;
mutable int m_B; //可修改 可变的
};
//const修饰对象 常对象
void test01() {
Person p;
p.ShowPerson();
const Person person; //常对象(对象前面加const)
//普通对象可访问不可修改
//person.m_A = 100; //常对象不允许修改普通的成员变量:常对象不能修改成员变量的值(不允许修改指针指向的值)
cout << person.m_A << endl;
//mutable对象可访问可修改
cout << person.m_B << endl;
person.m_B = 50; //但是常对象可以修改mutable修饰成员变量
cout << person.m_B << endl;
常对象只能访问常函数
//person.MyFunc(); //常对象不能调用普通的成员函数函数,因为普通成员函数可以修改属性,而常对象本身不能修改属性,已经违背了
person.ShowPerson();
}
int main() {
test01();
system("pause");
return 0;
}