本系列文章为黑马程序员C++教程学习笔记,前面的系列文章链接如下 C++核心编程:P1->程序的内存模型 C++核心编程:P2->引用 C++核心编程:P3->函数提高 C++核心编程:P4->类和对象----封装 C++核心编程:P5->类和对象----对象的初始化和清理
一、成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
我们先写一个空类,然后创建出一个对象,看看这个空对象占多大空间。
#include <iostream>
using namespace std;
#include <string>
class Person
{
};
void test01()
{
Person p;
cout << "size of p is: " << sizeof(p) << endl;
}
int main()
{
test01();
return 0;
}
运行,可以看到空对象大小为
1
1
1 字节。这是因为编译器为了区分空对象占内存的位置,给每个空对象也分配一个独一无二的内存地址。
当类中有成员变量时,看可能会占多大空间。我们这里在类中添加一个int类型的成员变量m_A。
#include <iostream>
using namespace std;
#include <string>
class Person
{
int m_A;
};
void test01()
{
Person p;
cout << "size of p is: " << sizeof(p) << endl;
}
int main()
{
test01();
return 0;
}
可以看到类对象的大小是4字节。 若包含了静态成员变量,则它不属于类对象,类型对象的大小不变 当我们添加静态和非静态成员函数时,类对象大小也不变,因为它们也不属于类对象上。
二、this指针
问题
在C++中成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码。那么问题是:这一块代码是如何区分那个对象调用自己的呢?
this指针
c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象。this指针是隐含每一个非静态成员函数内的一种指针。this指针不需要定义,直接使用即可。 this指针的用途: ----当形参和成员变量同名时,可用this指针来区分 ----在类的非静态成员函数中返回对象本身,可使用return *this
当形参和成员变量同名时,不能直接使用对应的名字来做修改,需要借助this指针
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age = " << p1.age << endl;
}
int main() {
test01();
return 0;
}
运行,可以看出当成员变量和形参相同时,通过this指针修改了成员变量的值
现在我们想增加一个成员函数,实现将一个对象的年龄加到另一个对象的年龄上。
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
void PersonAddPerson(Person &p)
{
this->age += p.age;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.PersonAddPerson(p1);
cout << "p2.age = " << p2.age << endl;
}
int main() {
test01();
return 0;
}
运行,可以看出结果正确
但是如果现在我想在一行多次调用这个成员函数,发现会报错,该怎样修改呢? 解决方案:我们这里的成员函数没有返回值,所以肯定会报错。如果每次执行PersonAddAge都能返回当前对象,那么就可以继续调用PersonAddAge,所以可以作如下修改:
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
Person& PersonAddPerson(Person p)
{
this->age += p.age;
return *this;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
cout << "p2.age = " << p2.age << endl;
}
int main() {
test01();
return 0;
}
运行,可以看到成功多次调用了成员函数 注意:如果将PersonAddAge以值返回的方式,则结果会不对。这是因为每次返回的不是本体,而是重新创建了一个新的对象。然后下次就会修改这个新的对象的成员变量的值,最后导致最开始调用的那个对象只修改了一次。
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
Person PersonAddPerson(Person &p)
{
this->age += p.age;
return *this;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
cout << "p2.age = " << p2.age << endl;
}
int main() {
test01();
return 0;
}
三、空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针。如果用到this指针,需要加以判断保证代码的健壮性
首先,我们确认空指针调用成员函数的情况
#include <iostream>
using namespace std;
class Person {
public:
void ShowClassName() {
cout << "我是Person类!" << endl;
}
};
void test01()
{
Person * p = NULL;
p->ShowClassName();
}
int main() {
test01();
return 0;
}
运行,可以发现空指针也调用了成员函数 但是如果成员函数中用到了this指针,就不可以了
#include <iostream>
using namespace std;
class Person {
public:
void ShowPerson() {
cout << mAge << endl;
}
public:
int mAge;
};
void test01()
{
Person * p = NULL;
p->ShowPerson();
}
int main() {
test01();
return 0;
}
运行,可以发现报错,因为传入的指针是为NULL 解决方案:因此,我们在成员函数中用到this,就加入判断。如果是空指针,就直接return出来。
class Person {
public:
void ShowClassName() {
cout << "我是Person类!" << endl;
}
void ShowPerson() {
if (this == NULL) {
return;
}
cout << mAge << endl;
}
public:
int mAge;
};
void test01()
{
Person * p = NULL;
p->ShowClassName();
p->ShowPerson();
}
int main() {
test01();
system("pause");
return 0;
}
运行,可以发现代码不崩
四、const修饰成员函数
常函数:
①成员函数后加const后我们称为这个函数为常函数 ②常函数内不可以修改成员属性 ③成员属性声明时加关键字mutable后,在常函数中依然可以修改
我们先测试一下this指针的用途,可以看到this指针本质上就是个指针常量。
#include <iostream>
using namespace std;
class Person {
public:
void ShowPerson(){
this->m_A = 100;
}
int m_A;
};
int main() {
Person p;
p.ShowPerson();
return 0;
}
运行,可以发现this可以修改指向的值,不能修改指向。
我们再来看下常函数和mutable的用处。可以看到常函数无法修改普通的成员变量,但是可以修改mutable修饰的成员变量。
#include <iostream>
using namespace std;
class Person {
public:
void ShowPerson() const {
this->m_B = 100;
}
public:
int m_A;
mutable int m_B;
};
int main() {
return 0;
}
常对象:
①声明对象前加const称该对象为常对象,也无法修改成员变量的值 ②如果成员变量加了mutable,则就可以修改 ③常对象只能调用常函数,因为普通函数可能会修改成员变量的值
我们看一下常对象的相关用法
#include <iostream>
using namespace std;
class Person {
public:
void MyFunc() const {}
void MyFunc2(){}
int m_A;
mutable int m_B;
};
void test01() {
const Person person;
person.MyFunc();
person.m_B = 100;
}
int main() {
test01();
return 0;
}
运行,可以发现常对象只能调用常函数,且不能修改普通成员变量,但是可以修改mutable修饰的成员变量。
|