一、成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储。C++编译器会给每个空对象也分配一个字节的空间,是为了区分空对象占内存的位置,每个空对象都有一个独一无二的内存地址。只有非静态成员变量才属于类的对象上
示例:空对象占用的内存空间为1
#include<iostream>
using namespace std;
//成员变量和成员函数是分开存储的
class Person
{
};
void test01()
{
Person p; //创建对象p
//统计对象p占多大空间
//空对象占用的内存空间为 1
//C++编译器会给每个空对象也分配一个字节的空间,是为了区分空对象占内存的位置,每个空对象都有一个独一无二的内存地址
cout << "size of p = " << sizeof(p) << endl;
}
int main()
{
test01();
return 0;
}
运行结果:空对象占用的内存空间为1,原因是C++编译器会给每个空对象也分配一个字节的空间,是为了区分空对象占内存的位置
示例:非静态成员变量所占空间
#include<iostream>
using namespace std;
//成员变量和成员函数是分开存储的
class Person
{
//编译器按照int来分配内存,分配4个字节
int m_A;//没有加static, 是非静态成员变量,属于类的对象上的
};
void test02()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
}
int main()
{
test02();
return 0;
}
运行结果: 非静态成员变量所占空间是4
示例:在上例基础上加入静态成员变量
#include<iostream>
using namespace std;
//成员变量和成员函数是分开存储的
class Person
{
//编译器按照int来分配内存,分配4个字节
int m_A;//没有加static, 是非静态成员变量,属于类的对象上的
static int m_B;//静态成员变量,类内声明,类外初始化,不属于类对象上
};
int Person::m_B = 0;
void test02()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
}
int main()
{
test02();
return 0;
}
运行结果:还是4个字节,是因为静态成员变量不属于某一个类的对象里面
示例:在上例基础上加入非静态成员函数:
#include<iostream>
using namespace std;
//成员变量和成员函数是分开存储的
class Person
{
//编译器按照int来分配内存,分配4个字节
int m_A;//没有加static, 是非静态成员变量,属于类的对象上的
static int m_B;//静态成员变量,类内声明,类外初始化,不属于类的对象上
void func() //非静态成员函数, 不属于类的对象上
{
}
};
int Person::m_B = 0;
void test02()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
}
int main()
{
test02();
return 0;
}
运行结果:加入非静态成员函数后,还是4个字节
示例:在上例基础上加入静态成员函数
#include<iostream>
using namespace std;
//成员变量和成员函数是分开存储的
class Person
{
//编译器按照int来分配内存,分配4个字节
int m_A;//没有加static, 是非静态成员变量,属于类的对象上的
static int m_B;//静态成员变量,类内声明,类外初始化
void func() //非静态成员函数
{
}
static void func2() //静态成员函数,不属于类的对象上
{
}
};
int Person::m_B = 0;
void test02()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
}
int main()
{
test02();
return 0;
}
运行结果:还是4个字节,是因为静态成员函数也不属于某一个类的对象里面
总的来说只有非静态成员变量属于类的对象上
二、this指针概念
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用同一份代码,那么问题是:这一块代码是如何区分哪个对象调用自己的呢?
C++通过提供特殊的对象指针,this指针解决上述问题。
this指针指向被调用的成员函数所属的对象。
this指针是隐含每一个非静态成员函数的一种指针
this指针不需要定义,直接使用即可
this指针的用途
1、当形参和成员变量同名时,可用this指针来区分
2、在类的非静态成员函数中返回对象本身,可使用return *this
示例:成员属性和传入形参的名称相同且没有使用this指针
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
age = age;
}
int age;
};
//1 解决名称冲突
void test01()
{
Person p1(18);
cout << "p1的年龄为:" << p1.age << endl;
}
//2 返回对象本身用*this
int main()
{
test01();
return 0;
}
此时发现阴影部分没有区分,相同阴影的age是一样的
运行结果:结果并不是18 ,是因为编译器认为三个age都是同一份,跟属性age不是一回事,所以并没有将属性age进行赋值操作
示例:成员属性和传入形参的名称相同且使用this指针
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
//this指针指向被调用的成员函数所属的对象。
this-> age = age;
}
int age;
};
//1 解决名称冲突
void test01()
{
Person p1(18);
cout << "p1的年龄为:" << p1.age << endl;
}
//2 返回对象本身用*this
int main()
{
test01();
return 0;
}
此时发现阴影部分有区分
运行结果:this指针指向被调用的成员函数所属的对象,p1在调用成员函数,所以this指向p1,谁调用Person(int age),this就指向谁。所以正常输出18。
示例:返回对象本身用*this
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this -> age = age;
}
//加入引用&每次返回一次就是返回p2
Person& PersonAddAge(Person& p) //若不加引用&,就是以值的方式进行返回,每返回一次就创建一个新的对象
{
this->age += p.age; //目的为了p1与p2的年龄相加
//this指向p2的指针,而*this指向的就是p2这个对象本体
return *this; //返回p2的本体要用引用的方式返回
}
int age;
};
//1 解决名称冲突
//2 返回对象本身用*this
void test02()
{
Person p1(10);
Person p2(10);
//链式编程思想,类似于cout<<""<<""<<""<<.....
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1); //想多加几次十岁
cout << "p2的年龄为:" <<p2.age<< endl;
}
int main()
{
test02();
return 0;
}
运行结果:实现了多次相加
三、空指针访问成员函数
C++中空指针也是可以调用成员函数的,但也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
示例:空指针调用成员函数没用this指针
#include<iostream>
using namespace std;
//空指针调用成员函数
class Person
{
public:
void showClassName()
{
cout << "this is Person class" << endl;
}
void showPersonAge()
{
cout << "age = " << m_Age << endl;
}
int m_Age;
};
void test01()
{
Person* p = NULL; //Person*类型的指针指向NULL
p->showClassName(); //空指针调用成员函数showClassName()
p->showPersonAge(); //空指针调用成员函数showPersonAge()
}
int main()
{
test01();
return 0;
}
运行结果:程序崩了
而且是因为这一行代码让程序崩溃
原因:这里默认是this->m_Age,但Person*类型是指向空指针,所以this没有指向确切的数据,所以发生错误
示例: 加以判断保证代码的健壮性
#include<iostream>
using namespace std;
//空指针调用成员函数
class Person
{
public:
void showClassName()
{
cout << "this is Person class" << endl;
}
void showPersonAge()
{
//修改方式:加入判断提高代码的健壮性
if (this == NULL)
{
return;
}
//这里默认是this->m_Age,但Person*类型是指向空指针,所以this没有指向确切的数据,所以发生错误
//报错原因是因为传入的指针是NULL
cout << "age = " << this->m_Age << endl;
}
int m_Age;
};
void test01()
{
Person* p = NULL; //Person*类型的指针指向NULL
p->showClassName(); //空指针调用成员函数showClassName()
p->showPersonAge(); //空指针调用成员函数showPersonAge()
}
int main()
{
test01();
return 0;
}
运行结果:正常调用
四、const修饰成员函数
常函数:
常:有些数据限定了只读状态
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数
示例:可以修改情况
class Person
{
public:
void showPerson()
{
m_A = 100; //可以修改
}
int m_A;
};
示例:不可以修改情况
分析:每一个成员函数内部都有一个this指针
this指针的本质 是指针常量 指针常量的指向是不可以修改的
this指针是不可以修改指针的指向的,this类似于Person * const this;
示例:常函数
#include<iostream>
using namespace std;
class Person
{
public:
//this指针的本质 是指针常量 指针常量的指向是不可以修改的
//const Person * const this;
//在成员函数后面加const, 本质修饰的是this指向, 让指针指向的值也不可以修改
void showPerson() const
{
this->m_B = 100; //加入mutable后即使在常函数中变量也可以修改
//this->m_A = 100; //不可以修改
//this = NULL; //this指针是不可以修改指针的指向的
}
int m_A;
mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值, 加上关键字mutable
};
void test01()
{
Person p;
p.showPerson();//当p对象去调用showPerson()时候,this指向的是p,不能指向别的地了
}
int main()
{
return 0;
}
示例:常对象
在对象前加const变为常对象,常对象不允许修改指针指向的值
#include<iostream>
using namespace std;
class Person
{
public:
//this指针的本质 是指针常量 指针常量的指向是不可以修改的
//const Person * const this;
//在成员函数后面加const, 本质修饰的是this指向, 让指针指向的值也不可以修改
void showPerson() const
{
this->m_B = 100; //加入mutable后即使在常函数中变量也可以修改
//this->m_A = 100; //不可以修改
//this = NULL; //this指针是不可以修改指针的指向的
}
void func()
{
m_A = 100; //可以修改非常变量的值(属性)
}
int m_A;
mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值, 加上关键字mutable
};
void test01()
{
Person p;
p.showPerson();//当p对象去调用showPerson()时候,this指向的是p,不能指向别的地了
}
//常对象
void test02()
{
const Person p; //在对象前加const变为常对象
//p.m_A = 100;
p.m_B = 100; //m_B是特殊值,在常对象下也可以修改
//常对象只能调用常函数
p.showPerson();
//常对象不可以调用普通的成员函数,因为普通成员函数可以修改属性
//p.func(); //调用普通函数,若可以修改则侧面修改了非常变量的值(属性),所以编译器是不给修改的
}
int main()
{
return 0;
}
常对象不可以调用普通的成员函数,因为普通成员函数可以修改属性,所以常对象只能调用常函数