1.成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
- 代码演示:
空对象所占用的内存空间
C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置。每个空对象也应该有一个独一无二的内存地址。
#include <iostream>
using namespace std;
class Person
{
public:
};
void test01()
{
Person p;
cout << sizeof(p) << endl; // 空对象占用一个字节
}
int main()
{
test01();
system("pause");
return 0;
}
- 输出结果:
1
请按任意键继续. . .
非空对象所占用的内存空间
1.非静态成员变量,属于类的对象上;
2.静态成员变量、静态成员函数和非静态成员函数,不属于类的对象上。
#include <iostream>
using namespace std;
class Person
{
public:
int m_A;
static int m_B;
void func() {}
static void func1() {}
};
int Person::m_B = 0;
void test02()
{
Person p1;
cout << sizeof(p1) << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
- 输出结果:
4
请按任意键继续. . .
2.this指针的用途
非静态成员变量是存放在对象上的,而静态成员变量和静态成员函数是所有对象共用的。只有非静态成员函数是不被所有对象共用,因此,在对象调用这个函数的时候,需要进行一个区分。
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么这一块代码是如何区分那个对象调用的是自己的函数的呢?
C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
。
this指针是隐含在每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途
:
- 当形参和成员变量同名时,可用this指针来区分(要有一个好的编码规范)
- 在类的非静态成员函数中返回对象本身,可用return *this
- 代码演示:
当形参和成员变量同名时,可以用this指针来区分
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
// this指针指向 被调用的成员函数 所属的成员函数
this->age = age; //当形参与成员变量同名时,this指针可以用来区分形参与成员变量
//age = age; //形参与成员变量同名,输出会出现错误(乱码)
}
int age;
};
// 1. 解决名称冲突
void test01()
{
Person p(18);
cout << "年龄 = " << p.age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
- 输出结果:
年龄 = 18
请按任意键继续. . .
在类的非静态成员函数中返回对象本身,可用return *this
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age; //当形参与成员变量同名时,this指针可以用来区分形参与成员变量
}
// 函数返回值类型如果为void(无符号类型),则不能进行多次加操作,进行多次加操作则会报错(返回的不是对象,不能调用成员函数)
// 函数返回的如果是值,则会出现尽管是链式相加,但是结果还是两个数相加的结果(18+18=36 18+18=36)
// 函数只有返回的是引用,才能实现链式相加(18+18+18=54)
Person& PersonAddPerson(Person &p)
{
this->age += p.age;
return *this;
}
int age;
};
// 2. 返回对象本身
void test02()
{
Person p1(18);
Person p2(18);
// 链式编程思想
p2.PersonAddPerson(p1).PersonAddPerson(p1);
cout << "p2的年龄 = " << p2.age << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
- 输出结果:
p2的年龄 = 54
请按任意键继续. . .
【注意】
:
- 若非静态成员函数返回的是void类型的值,则代码出错,因为非对象不能调用成员函数;
- 若非静态成员函数返回的是Person类型的值,则得到的结果会是36,而不是54,相当于每调用一次成员函数得到一个新的对象
???
; - 只有当非静态成员函数返回的是Person的引用,才会得到一个正确的结果。
3.空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
- 代码演示:
#include <iostream>
using namespace std;
//空指针调用成员函数
class Person
{
public:
void showClassName()
{
cout << "this is Person Class" << endl;
}
void showPersonAge()
{
// 报错(读取访问权限冲突),原因是传入的指针为空指针,未指向任何实体,因此不能够访问属性
// 为了提升代码的健壮性,可以写一个if else语句,这样尽管不能访问,但是可以直接退出,不会出现报错。
if (this == NULL)
{
return;
}
cout << "PersonAge is " << this->m_Age << endl;
}
int m_Age;
};
void test01()
{
Person* p = NULL;
p->showClassName();
p->showPersonAge();
}
int main()
{
test01();
system("pause");
return 0;
}
- 输出结果:
this is Person Class
请按任意键继续. . .
4.const修饰成员函数
常函数
:
- 成员函数后加const后我们称这个函数为常函数;
- 常函数内不可以修改成员属性;
- 成员属性申明时加关键字mutable后,在常函数中依然可以修改。
常对象
:
- 申明对象前加const称该对象为常对象;
- 常对象不允许修改一般的成员属性,只能修改带有关键字mutable的成员属性;
- 常对象只能调用常函数,不能调用普通的成员函数。
- 代码演示:
常函数
#include <iostream>
using namespace std;
class Person
{
public:
// this指针的本质是指针常量 只能改变指针指向变量的值,但是不能改变指针的指向
// const Person * const this
// 在成员函数后面加const,修饰的是this指针,指针指向的值也不能修改了
void showPersonName() const
{
this->m_A = 100; // 特殊变量(mutable)成员变量可以在常函数中修改
//this->m_B = 100; //报错,常函数内不能修改成员属性
//this = NULL; //报错,this指针只能改变指针指向变量的值,不能改变指针的指向
}
// 成员属性的申明时前加关键字mutable后,成员属性在常函数中仍能修改
mutable int m_A;
int m_B;
};
// 常函数
void test01()
{
Person p;
p.showPersonName();
cout << p.m_A << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
- 输出结果:
100
请按任意键继续. . .
常对象
#include <iostream>
using namespace std;
class Person
{
public:
void showPersonName() const
{
this->m_A = 100;
}
void func()
{
cout << "常对象不能调用普通成员函数!" << endl;
}
mutable int m_A;
int m_B;
};
// 常对象
void test02()
{
const Person p1; // 在对象前加一个const则得到一个常对象,常对象不允许修改一般的成员属性
p1.m_A = 200; //常对象可以修改特殊的成员属性(mutable成员属性)
cout << p1.m_A << endl;
p1.showPersonName();
cout << p1.m_A << endl;
//p1.m_B = 100; //报错,常对象也不能修改一般的成员属性
//p1.func(); //报错,常对象不能调用普通成员函数,因为其可以修改属性
}
int main()
{
test02();
system("pause");
return 0;
}
- 输出结果:
200
100
请按任意键继续. . .