本篇来学习对象模型和this指针,对象模型主要是了解对象的存储模型。在C++中,类内的成员变量和成员函数是分开存储的,只有非静态成员变量才是属于类的对象上,其他都和类存在不同内存区域。
1.一个类的空对象占用内存是多大字节
这里我们先写一个代码,通过sizeof()函数来打印一个空的类对象占用内存是多少字节。
#include <iostream>
using namespace std;
class Person
{
};
void test01()
{
Person p;
cout << "空对象P所占内存大小为:" << sizeof(p) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
上面定义了一个Person类,没有写成员变量和成员方法。然后直接初始化一个对象p,那么我们问题就来了,这么一个空的类对象所占用的内存大小是多少?我们可以能会猜测1 或者 0 或者 4等。
因为C++编译器会给每一个空对象分配一个空间,是为了区分位置,每一个对象都有独一无二内存空间,这个空间大小就是1字节。
2.一个类只定义一个int类型的变量的内存大小
在上面代码基础之上,添加非静态成员变量 int m_A, 看看这个类对象所占的内存是多少。
#include <iostream>
using namespace std;
class Person
{
int m_A;
};
void test01()
{
Person p;
cout << "空对象P所占内存大小为:" << sizeof(p) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果就是4字节,因为C++编译器检测到这个类里面定义了一个int类型的变量,所以这个类对象所占内存就是4字节。这个结果,侧面证明了非静态成员对象是属于这个类对象上的。
3.静态成员变量不属于类对象上
通过下面代码的例子,创建一个静态成员变量,并在类外进行初始化,看看这个对象p的内存是多少。
#include <iostream>
using namespace std;
class Person
{
int m_A;
static int m_B;
};
int Person::m_B = 100;
void test01()
{
Person p;
cout << "对象P所占内存大小为:" << sizeof(p) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行之后
这个结果还是4字节,说明静态成员变量m_B就不在对象p上,证明了静态成员变量和非静态成员变量是存储在不同区域。
4.静态成员函数和非静态成员函数也不在类对象上
接下来,我们来证明一个类的非静态成员函数也不属于类上存储。
#include <iostream>
using namespace std;
class Person
{
int m_A;
static int m_B;
void func()
{
}
};
int Person::m_B = 100;
void test01()
{
Person p;
cout << "对象P所占内存大小为:" << sizeof(p) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行得到
运行结果依然是4个字节,说明非静态成员函数也不属于类对象上。其实不管非静态函数还是静态函数都不属于类对象上存储。下面新添加一个静态成员函数,再次运行代码看看对象p的内存大小。
#include <iostream>
using namespace std;
class Person
{
int m_A;
static int m_B;
void func()
{
}
static void func2()
{
}
};
int Person::m_B = 100;
void test01()
{
Person p;
cout << "对象P所占内存大小为:" << sizeof(p) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
结论:不管静态成员函数还是非静态成员函数,都不在类对象上存储。那边C++是如何区分不同类对象下调用同一个函数的呢,接下来就要学习this指针来回答这个问题。
5.this指针
C++通过this指针来区分很多对象调用同一个函数。this指针指向被调用的成员函数所属的对象,this指针是隐含每一个非静态成员函数的一种指针,this指针不需要定义,直接使用即可。
this指针的作用
- 当形参和成员变量同名时,可用this指针来区分。
- 在类的非静态成员函数中返回对象本身,可使用return *this
先用一段代码来描述this指针区分形参和成员变量同名的问题。
#include<iostream>
using namespace std;
class Person
{
public:
int age;
Person(int age)
{
age = age;
}
};
void test01()
{
Person p(18);
cout << "p的年龄为:" << p.age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行代码后
根据我们前面学习的知识,难道这里不是调用有参构造,年龄输出18吗?为什么输出这个。
问题就出在这个有参构造,age = age,这个时候类的变量和函数的形式参数名一样,造成C++认为这两个age是一样,并没有把类变量赋值成功。为了解决这个问题,我们可以在定义类变量的时候加上m_age 或者Age,这样来区分。第二种办法,就是使用this指针,下面来看看this指针的代码。
#include<iostream>
using namespace std;
class Person
{
public:
int age;
Person(int age)
{
this->age = age;
}
};
void test01()
{
Person p(18);
cout << "p的年龄为:" << p.age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
this是一个指针,所以这里是this->来表示。这个时候this表示当前这个被调用函数的所属的对象,也就是代码中的p对象,这句话的理解很关键,上面用红色字体标注出来。
使用return *this返回对象本身
有时候我们需要返回对象本身,特别是一些在链式编程的语法上。来看看下面代码。
#include<iostream>
using namespace std;
class Person
{
public:
int age;
Person(int age)
{
this->age = age;
}
//解引用的方式做一个返回
Person& personAddAge(Person &p)
{
this->age += p.age;
return *this;
}
};
void test01()
{
Person p1(10);
Person p2(10);
p2.personAddAge(p1).personAddAge(p1).personAddAge(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果就是40,这个不断后面调用方法就是链式编程的特点。注意上面返回的是一个解引用,并不是一个值,如果代码修改返回值,运行结果是什么呢?
#include<iostream>
using namespace std;
class Person
{
public:
int age;
Person(int age)
{
this->age = age;
}
//返回值
Person personAddAge(Person &p)
{
this->age += p.age;
return *this;
}
};
void test01()
{
Person p1(10);
Person p2(10);
p2.personAddAge(p1).personAddAge(p1).personAddAge(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果是20,也就是调用PersopnAddAge一次的效果。
第一次调用,age=20,这个时候需要返回p2的一个值,这个值和p2不是同一个,如果是引用就是同一个,值就是调用拷贝构造函数,新的对象默认age等于0,所以后面执行多次还是等于20.