前言
我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,当我们实例化出多个对象后,这些同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?通过this指针
c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象,谁调用成员函数this指针就指向谁
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
1.this指针的用途:
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
示例1:当形参和成员变量同名时,可用this指针来区分
#include<iostream>
using namespace std;
class Person {
public:
Person(int age) {
age = age;
}
int age;
};
void test01() {
Person p(25);
cout << "p.age=" << p.age << endl;
}
int main() {
test01();
return 0;
}
可以看出,当形参与成员变量同名时,无法正常赋值
解决方法如下:
将
Person(int age) {
age = age;
}
改成:
Person(int age) {
this->age = age;
//也就是左边age前加个this->,代表这是成员变量
//this指针指向被调用的成员函数所属的对象,谁调用成员函数this指针就指向谁
}
示例2:在类的非静态成员函数中返回对象本身,可使用return *this
#include<iostream>
using namespace std;
#include<string>
class Person {
public:
// 1.解决名称冲突
Person(int age) {
this->age = age;
}
int age;
// 2.返回对象本身用*this
// 修改后的函数如下
// 必须要返回引用的方式Person&
// 不能返回一个普通对象Person(这相当于返回值,如果返回值,return语句会拷贝构造出新的对象,就不是p2了)
Person& PersonAddAge(Person& p) {
this->age = this->age + p.age;
// this指向p2的指针,而*this指向的就是p2这个对象本体
// return*this返回的就是调用这个成员函数的对象
cout << "如果函数返回的是引用,那么&(*this)=" << int(&(*this)) << endl<<endl;
return *this;
}
};
// 1.解决名称冲突
void test01() {
Person p1(18);
cout << "p1的年龄为:" << p1.age << endl;
}
// 2.返回对象本身用 *this
void test02() {
Person p1(10);
Person p2(10);
// 使用一次PerosnADDAge函数,发现可以正常赋值
//p2.PersonAddAge(p1);
// 如果一次不够我想要多次使用呢?
// p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);可不可以呢?
// 答案是可以的,但是PersonADDAge得做修改
// 上面体现了链式编程思想,cout就是这样子的
cout << "&p2=" <<int(&p2) << endl;
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
如果把:
Person& PersonAddAge(Person& p) {
this->age = this->age + p.age;
// this指向p2的指针,而*this指向的就是p2这个对象本体
// return*this返回的就是调用这个成员函数的对象
cout << "如果函数返回的是引用,那么&(*this)=" << int(&(*this)) << endl<<endl;
return *this;
}
改成:
Person PersonAddAge(Person& p) {
this->age = this->age + p.age;
cout << "如果函数返回的是值,那么&(*this)=" << int(&(*this)) << endl<<endl;
return *this;
}
我们发现:p2的年龄变为了20,而且调用PersonAddAge函数中只有第一次的*this一样( 这也就解释了为什么p2.age=20,因为 * this只指向了一次p2,p2对象的PersonAddAge函数其实只加了一次,后面加的两次已经变成新对象里的值了),所以此时this并不是指向p2了,return语句会拷贝构造出跟p2数据一样但地址不同的新对象
总结:
1.在类的非静态成员函数中返回对象本身,可使用return *this
2.但必须返回的是引用(Person&),不能返回的是一个值(Person)。
3.cout<<体现了链式编程思想,可以无限追加
2. const修饰成员函数以及this指针的本质
常函数:
- 成员函数后加const后我们称为这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
void showPerson() const
{ // 特殊申明
this->m_C = 100;// 不会报错
this->m_B = 100;报错
this->m_A = 100;报错
// 本质上this指针是个指针常量,它的指向不可以修改,this以及指向一个对象了,不能再让它指向空,不过this指针指向的值可以改
// const Person *const this,如果再加个const那么它指向的值也不可以改了
// this = NULL;//this指针是指针常量,它的指向不可以更改,他已经指向一个对象了。
cout << m_C << endl;
}
int m_A;
int m_B;
mutable int m_C;//特殊变量,即使在常函数中,也可以修改这个值
常函数的本质:在成员函数后加const,修饰的是this指针的指向,让this指针指向的值也不可以改了
常对象:
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
int m_A;
int m_B;
mutable int m_C;//特殊变量,即使在常函数中,也可以修改这个值
// 常函数
void showPerson() const
{ // 特殊申明
//this->m_C = 100;// 不会报错
//this->m_B = 100;//报错
//this->m_A = 100;//报错
// 本质上this指针是个指针常量,它的指向不可以修改,所以this不能指向空,但this指针指向的值可以改
// const Person *const this。this指针的指向和指向的值都不可以改
// this = NULL;//不能改变this指针的指向,因为this指针是指针常量
cout << m_C << endl;
}
// 普通函数
void func() {
m_A = 45;
}
// 常对象
void test02() {
const Person p;// 在对象前加const,变为常对象
p.m_A = 100;//不可修改,常对象可以访问成员变量,但是不能改变成员变量的值
// 也就是说常对象访问的成员变量是个常量,除非这个成员变量在声明时加了关键字mutable
p.m_C = 500;//可以修改,如果再成员变量前加个mutable,那么常对象就可更改这个成员变量的值
p.showPerson();
// 常对象只能调用常函数
//报错
p.func();
}
常对象的本质:在声明对象前加const称该对象为常对象,常对象也让this指针指向的值不可以改
完整代码:
#include<iostream>
using namespace std;
#include<string>
// const修饰成员函数
class Person {
public:
Person(int a, int b, int c);
// this指针的本质是 指针常量
void showPerson() const
{ // 特殊申明
//this->m_C = 100;// 不会报错
//this->m_B = 100;//报错
//this->m_A = 100;//报错
// 本质上this指针是个指针常量,它的指向不可以修改,所以this不能指向空,但this指针指向的值可以改
// const Person *const this
// this = NULL;
cout << "m_A=" << m_A << " " << endl
<< "m_B=" << m_B << " " << endl
<< "m_C=" << m_C << " " << endl;
}
void func() {
cout << "this func()" << endl;
}
int m_A;
int m_B;
mutable int m_C;//特殊变量,即使在常函数中,也可以修改这个值
};
Person::Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}
//普通对象
void test01() {
Person p(22,23,56);
cout << "普通对象p信息如下:" << endl;
p.showPerson(); cout << endl;
p.m_A = 52; p.m_B = 32; p.m_C=65;
cout << "普通对象p信息修改后:" << endl;
p.showPerson(); cout << endl;
}
// 常对象
void test02() {
const Person p1(66,77,89);// 在对象前加const,变为常对象。常对象和普通对象一样初始化
p1.m_A; p1.m_B;//不可修改
cout << "常对象p1信息如下:" << endl;
p1.showPerson(); cout << endl;
p1.m_C = 500;//可以修改
cout << "将常对象p1信息修改后:" << endl;
p1.showPerson();
// 常对象只能调用常函数
//报错
//p.func();
}
int main() {
test01();
test02();
system("pause");
return 0;
}
常函数和常对象总结
总结:常函数
- 成员函数后加const后我们称为这个函数为常函数
void showPerson() const{},此时this指针由Person * const this变成const Person * const this,无法修改this指针指向的值- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
mutable int m_C;
常函数的本质:在成员函数后加const,修饰的是this指针的指向,让this指针指向的值也不可以改了
总结:常对象
- 声明对象前加const称该对象为常对象
const Person p1- 常对象只能调用常函数
- 常对象和普通对象那样正常赋值,不过常对象里的成员变量是常量,不可修改。除非这个成员变量声明时加了关键字mutable(注意成员函数前面不可以加关键字mutable,常对象要想调用成员函数,必须把它变为常函数)
总结:this指针
this指针的本质:指针常量。指针的指向不可以改,指针指向的值可以改。
this指针本质代码表示:类名 * const this=&实例化对象名;
再加个const变成const 类名 *const this=&实例化对象名,这样它指向的值也不可以改了。
怎么把前面这个const加进去,使this指针指向的值也不可以改呢?
只需把成员函数变为常函数或者创建常对象,即void showPerson()变为void showPerson() const{}后或者创建常对象const Person p,编译器会将this指针由Person * const this变成const Person * const this,此时就无法修改this指针指向的值
3. 空指针访问成员函数(this指针的一个坑)
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
示例1:运行下面代码,看看有无错误
#include<iostream>
using namespace std;
#include<string>
// 空指针调用成员函数
class Person {
public:
void showClassName() {
cout << "this is Person class" << endl;
}
/*void showPersonAge() {
// m_age是个属性,在属性前面都默认加了个this->
// 报错原因是因为传入的指针是个NULL
cout << "age=" << m_age//其实完整的是this->m_age
<< endl;
}
int m_age;*/
};
void test01() {
Person* p = NULL;//创建一个空指针
p->showClassName();
//p->showPersonAge();
}
int main() {
test01();
system("pause");
return 0;
}
正常运行
示例2:
#include<iostream>
using namespace std;
#include<string>
// 空指针调用成员函数
class Person {
public:
void showClassName() {
cout << "this is Person class" << endl;
}
void showPersonAge() {
// m_age是个属性,在属性前面都默认加了个this->
// 报错原因是因为传入的指针是个NULL
cout << "age=" << m_age//其实完整的是this->m_age
<< endl;
}
int m_age;
};
void test01() {
Person* p = NULL;//创建一个空指针
p->showClassName();
p->showPersonAge();
}
int main() {
test01();
system("pause");
return 0;
}
报错,原因是我们创建的对象指向空,this指针是个空指针。
空指针怎么能访问类里面的属性呢
空指针是不能访问到类里面的属性的,我们的改进措施也仅仅是让程序不崩而已,无法做到让个空指针打印出类里的属性出来
解决方法如下:
将
void showPersonAge() {
// m_age是个属性,在属性前面都默认加了个this->
// 报错原因是因为传入的指针是个NULL
cout << "age=" << m_age//其实完整的是this->m_age
<< endl;
}
int m_age;
};
改成:
void showPersonAge() {
// m_age是个属性,在属性前面都默认加了个this->
// 报错原因是因为传入的指针是个NULL
// 解决方案
if(this == NULL)
{
return;
}
// 如果this为空指针,下面的代码就不会执行了
// 程序也就不会崩溃了
cout << "age=" << m_age//其实完整的是this->m_age
<< endl;
}
int m_age;
};
判断代码:
if(this == NULL)
{
return;
}
总结:
1.C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
2.如果用到this指针,需要加以判断保证代码的健壮性
3.空指针的作用仅仅就是调用一下成员函数而已,他无法访问成员属性(更不可能赋值)所以如果在成员函数中用到了空的this指针,必须加判断代码。
4.空指针平常基本用不到,不要实例化个空指针出来。