目录
1、各种情况下一个对象所占存储空间的变化
(1)如果class Person里不含任何东西,此时新建一个Person p对象,则p所占空间是1个字节;
(2)如果class Person里包含一个int m,则p占4个字节;
(3)如果class Person里包含一个static int m,由于static类型是存储在全局中,因此不占某个对象的空间;
(4)如果class Person里包含一个函数void func(){ },则仍然只占1个字节,static void func()同理
由上面可知,所有成员函数都是分开存储的,不记在某个对象的存储空间中,只有成员变量是算在对象的存储空间里的。
2、this指针
this有两种必须使用的情况:
(1)当成员函数的形参和成员变量同名时,必须用this->age的形式指代当前对象的age变量;
(2)当非静态的成员函数的返回值是对象本身时,要return* this。这种情况一般用于实现链式编程。
静态成员函数没有this指针,因为所有同类型的对象都使用同一个静态成员函数,不需要通过this指针来区别是哪个对象在调用这个函数
先看第一种情况:
如果不用this指针,则会出现错误:
再看第二种情况:
为了能链式调用,则必须让p1.AddAge(10)的返回值是一个Person类型
下面是修改后的AddAge函数,里面存在错误
最容易发现的错误是返回值。this是一个指针,它定义的方式为Person* const this(const表示this指针无法改变指向),这是编译器隐式定义给Person类的。因此,return this返回的是一个地址,它的类型不是Person,这里应该改成return *this。
修改之后成功运行,但是结果才20,理论值应该是40
这是另一个不容易发现的错误: 上面这个函数返回的虽然是一个Person类型的对象,但是通过值传递的方式返回。以值的方式返回局部对象会调用拷贝构造函数,从而生成新的匿名变量p2,之后就是调用p2.AddAge(10).AddAge(10),再然后是p3.AddAge(10), 虽然最后p4 = 40, 但是没有影响p2里面的age属性。
要修改p2里的age,则需要返回引用类型,如下图:
源代码放在下面
#include<iostream>
using namespace std;
class Person{
public:
Person(int age){
this->age=age;
}
Person& AddAge(int age){
this->age+=age;
return *this;
}
int age;
};
int main()
{
Person p1(10);
p1.AddAge(10).AddAge(10).AddAge(10);
cout<<p1.age<<endl;
return 0;
}
3、空指针访问成员函数
#include<iostream>
using namespace std;
class Person{
public:
void showClassName()
{
cout<<"Person"<<endl;
}
void showAge()
{
cout<<age<<endl;
}
int age;
};
int main()
{
Person p1=NULL;
p1.showCLassName();
p1.showAge();
return 0;
}
这里将p1定义为空,则p1的this指针也指向空, 由于成员函数里的age在编译后默认变成
this->age,空指针没法找到age,因此函数showAge会报错无法运行。不过showClassName由于没有用到this指针,可以运行。虽然在devC++中只是warning,可以运行,但是其它一些编译器则无法运行。
4、类中的const修饰
这里const实际上是修饰的this指针,原来this指针是这么定义的:Person* const this,上面的const让它变成了:const Person* const this,意味着this指针指向不变,它指向的内容也不改变。
给height变量前加上mutable,则它可以在常函数、常对象中被修改。
在main函数里定义一个常对象,下图可见无法修改这个对象的age,但是可以修改常对象的mutable变量。同时,常对象也无法调用常函数以外的成员函数,这是因为:非常函数的成员函数可以对这个对象的所有实例变量进行修改,如果常对象可以调用非常函数的话,容易造成误操作,不利于安全性。