类和对象
C++对象模型和this指针
成员变量和成员函数的存储
C++中的成员变量和成员函数是分开存储的,只有非静态成员变量才属于类的对象上
class Person{
int m_Age;//非静态成员变量
static int m_B;//静态成员变量
void func(){ //不属于类的对象上
}
static void func2(){}
};
int Person::m_B=15;
void test01(){
Person p;
cout<<sizeof(p) <<endl;
//空对象占用内存空间为1
//C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置,
//每个空对象也应该有一个独一无二的内存地址
}
Person 的对象p只有 m_Age的大小也就是4个字节。
this指针:
在之前的笔记中写过, 每个非静态成员变量只诞生一份实例,也就是说多个同类对象共用一块代码,那么问题来了,这一块代码是如何区分到底是哪个对象调用了自己的?
那么C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用成员函数所属的对象。
this指针特性:
this指针是隐含每一个非静态成员函数内的一个指针
this指针不需要定义,直接使用即可
用途:
1.当行参和成员变量同名时,可以用this来区分。
class Person{
public:
Person(int age){
age=age;
}
int age;
};
void test01(){
Person p;
cout<<p.age<<endl;
}
int main(){
test01();
return 0;
}
上述例子中运行后会提示错误,这个错误就是因为age并不清楚哪个是成员变量哪个是参数传入的。
那就可以用this来区分一下。
//把
age=age;
//改为
this->age=age;
在类的非静态成员函数中返回对象本身,可使用return *this
class Person{
public:
Person(int age){
this->age=age;
}
void addAge(Person p){
this->age+=p.age;
}
int age;
};
//2.返回对象本身用*this
void test02(){
Person p1(10);
Person p2(15);
p2.addAge(p1);
cout<<p2.age<<endl;
}
此时的运行结果是10+15=25,那如果我想要多加几次,可不可以直接:
p2.addAge(p1).addAge(p1);
这样肯定是不行,从语法来说就不对因为我们返回值是void,但是我们最终目的是多加几次,应该如何做呢?
如果我们让这个函数执行结果return的是p2本身,执行一次后就保存在p2了,是否就可以多次执行了
class Person{
public:
Person(int age){
this->age=age;
}
Person& addAge(Person &p){
this->age+=p.age;
return *this;
}
int age;
};
void test02(){
Person p1(10);
Person p2(15);
p2.addAge(p1).addAge(p1);
cout<<p2.age<<endl;
}
这个时候代码就不报错了,结果就变成了35了。当然依次类推,由于返回的是本身。所以如果我们这样加结果是多少呢?
p2.addAge(p1).addAge(p1).addAge(p2);
后面再跟一个参数是p2的addAge,结果应该是70,因为前面p2年龄已经是35了,又传入了35加起来就是70了。
如果上面的返回值不是引用了,而是值,那么结果就会变为25,因为每次 addAge
都返回一个临时对象,并且每次调用都在新的临时对象上进行,因此这些操作都不会影响 p2
本身。
p2
的 age
在第一次调用后变为 25,之后的操作不再对 p2
本身产生影响。
这被称为是链式编程思维。
空指针访问成员函数
class Person{
public:
void showClassName(){
cout<<"1111 Person Class"<<endl;
}
void showPersonAge(){
cout<<m_Age<<endl;
}
int m_Age;
};
void test01(){
Person *p =NULL;
p->showClassName();
p->showPersonAge();
}
这里我们运行后程序会中断,原因在于showPersonAge()
这个函数身上。因为在
cout<<m_Age<<endl;
其实默认是有一个this->的
cout<<this->m_Age<<endl;
但是因为p是个空指针,所以会找不到这个数据,这当然会出错。
为了程序不断掉,我们可以这样做
void showPersonAge(){
if(this==NULL)
return;
cout<<m_Age<<endl;
}
加一个空指针的判断。
const修饰成员函数
常函数:
- 加上const修饰的函数称之为常函数
- 常函数不能修改成员属性(只读)
- 成员属性声明时加上关键字mutable后,在常函数中依然可以修改
class Person{
public:
void showPerson()const
{
m_A=100;
}
int m_A;
};
const修饰后,我们就无法对m_A进行修改了。
我们怎么理解呢?
其实原函数也可以写为
void showPerson()const
{
this->m_A=100;
}
每个成员函数都有一个this指针,this指针的本质就是指针常量,指针的指向是不可修改的。如果你在函数内部对this指针赋值,会报错,因为它是const的其实对于本例this等价于Person * const this。那如果想要this指向的值都不可修改就要在Person前面再加上const,就变成了:
const Person * const this
这个const其实就是void showPerson() const。其本质修饰的就是this指针指向的值。
同样,我们想要在常函数中也可以修改这个值的话。就加上mutable关键字
class Person{
public:
void showPerson()const
{
m_A=100;
}
mutable int m_A;
};
常对象:
- 加上const修饰的对象称为常对象
- 常对象只能调用常函数
class Person{
public:
void showPerson()const
{
m_A=100;
// m_B=1000;
}
mutable int m_A;
int m_B;
};
//常对象
void test01(){
const Person p;
p.m_B=100;//报错
}
由于const修饰了p,m_B并不是const修饰的,所以这里也会报错。
但是
p.m_A=100;//就可以了因为有mutable修饰。
接下来我们在Person类中加上一个空的函数
void func(){
}
使用静态修饰的对象p来调用,同样也会报错,因为常对象只能调用常函数,由于普通成员函数可以修改属性值,但是常函数不能修改值,如果允许我们这样做就会发生冲突了。
标签:p2,const,对象,age,笔记,Person,C++,void,指针 From: https://blog.51cto.com/u_16160587/11906993