目录
在C++的类与对象中,有6个默认的成员函数,就是即便用户不编写定义,编译器也会自动生成的成员函数。
其中,有两个默认成员函数是,实现取对象地址的功能。因为实际中,根据类创建对象时,
有的对象设定是可读可写的;而有的对象要求只可读不可更改。对于这类对象,在创建对象时,会加上 const 修饰,表示为常量对象。而如果只使用一种取地址函数,对于这两种类型的取地址时,会有权限的放大缩小的问题。
所以分别针对这两种类型,类里面提供了两种默认的取地址成员函数,分别是
普通对象的取地址 和 const对象的取地址。下面先对普通对象取地址的成员函数,展开学习。
( ps:
普通对象,没有用const修饰的对象
const对象,用const修饰的对象
)
注意: 取地址“ & ” 是一元运算符,所以对对象的取地址,本质上就是运算符重载函数。重载的操作符是 operator。
普通对象的取地址函数
作用
获取不是const修饰的对象的地址。
函数特征
函数原型为: 返回值 operator&(参数) 。
① 函数名 为 operator&;
② 没有参数 ;
③ 返回类型是类型指针。
下面是函数的模型:
#include <iostream>
using namespace std;
class Date
{
public:
// 获取普通对象地址的函数
Date* operator&() // 实际上被编译器处理为: Date* operator&(Date* this)
{
return this;
}
};
以上,便是对于日期类Date,普通对象(没用const修饰的对象)的取地址函数。
下面来看一下,显式普通对象取地址函数的效果
显式普通对象的取地址函数
例子程序:
#include <iostream>
using namespace std;
class Date
{
public:
// 获取普通对象地址的函数
Date* operator&() // 实际上被编译器处理为: Date* operator&(Date* this)
{
return this;
}
};
int main()
{
Date d1;
cout << &d1 << endl; // 打印输出对象d1的地址
return 0;
}
运行结果:
控制台成功输出对象d1的地址。
这是对于普通对象,用户自己定义的普通对象取地址的函数的效果。
那么,当用户没有定义这一特殊函数,使用编译器自行生成的,会是什么效果呢?我们来进行验证:
隐式普通对象的取地址函数
例子程序:
#include <iostream>
using namespace std;
class Date
{
};
int main()
{
Date d1;
cout << &d1 << endl; // 打印输出对象d1的地址
return 0;
}
运行结果:
我们发现,即便是没有编写定义对于普通对象取地址的函数。编译器使用自行生成的默认取地址函数,依旧能获取对象d1的地址。
因此对于这个默认成员函数,一般都不用用户编写定义,使用编译器自行生成的取地址函数即可实现效果。除非是一些特殊情况,比如不想创建出的对象的地址被获取,那么便可以编写定义显式取地址函数,然后在函数里面,返回空指针nullptr。总之,特殊情况特殊对待、设计。一般情况下,使用编译器自行生成的即可。
以上,我们实现了对于普通对象的取地址。
那么对于const修饰的对象,是否能通过以上的成员函数获取到常量对象的地址呢?如:
#include <iostream>
using namespace std;
class Date
{
};
int main()
{
Date d1;
const Date d2; // 常量对象d2
cout << &d1 << endl; // 打印输出对象d1的地址
cout << &d2 << endl; // 打印输出对象d2的地址
return 0;
}
结果显示:
对象d2的地址,一样被输出打印出来了,怎么回事?
答:那是因为,编译器自行生成的对于常量对象取地址的成员函数,起到的作用。
下面对,这一默认成员函数,const修饰的对象取地址函数,进行学习。
const对象的取地址函数
作用
获取const修饰的对象的地址
函数特征
函数原型为: 返回值 operator&(参数) const 。
#include <iostream>
using namespace std;
class Date
{
public:
// 获取const修饰的对象地址的函数
const Date* operator() const // 编译器处理成: const Date* operator(const Date* this)
{
return this;
}
};
首先,我们现弄明白非得要有这个成员函数,使用上面的成员函数,难道就不能获取常量对象的地址吗?答案肯定是不能的。原因如下:
#include <iostream>
using namespace std;
class
{
public:
// 普通对象取地址函数
Date* operator&() // 会被编译器处理为: Date* operator(Date* this)
{
cout << "operator&()" << endl;
return this;
}
// const修饰的对象取地址函数
const Date* operator&() const // 会被编译器处理为: Date* operator(const Date* this)
{
return this;
}
}:
int main()
{
Date d1; // 普通对象d1
const Date d2; // 常量对象d2
&d1; // 实际上编译器会编译成: d1.operator&(&d1)
&d2; // 实际上编译器会编译成: d2.operator&(&d2)
return 0;
}
要点:
注意程序上给注释,当我们要获取对象d1的地址&d1时,编译器会将去调用对象类里的成员函数operator&() ,
并且将d1的地址传过去。在类中定义的显式普通对象取地址的成员函数中,函数中隐藏了一个参数:Date类型的指针 this 。 这时 this 指针和传过来的参数d1的地址类型匹配(d1地址存的数据类型是Date),因此能成功调用该函数,并且在函数里面返回this指针(对象的地址)。
而对于对象d2来说,如果获取对象d2的地址&d2,调用的也是成员函数operator&()的话,会出现权限放大的问题。因为 operator&() 函数参数中隐藏的指针 this 是Date类型,而d2地址中存储的数据是const Date类型,是可读不可写的数据类型。 将可读不可写的地址传给可读可写指针,将会令指针的权限放大,而这是错误。所以为了解决这一问题,便有了const修饰的对象取地址的成员函数。
而const修饰的对象取地址的成员函数解决的方法就是,参数匹配。将 this 指针的类型也设置为const Date类型,那么将不会出现权限放大的问题。又因为 this 指针是隐藏的参数,所以不可能在参数里面实现。因此 标准规定 ,在函数的旁边加个 const 即可。编译器会自行生成函数参数为: const Date* this 的格式,因此便解决了这一问题。
显示const对象的取地址函数
例子程序:
#include <iostream>
using namespace std;
class Date
{
public:
// const对象的取地址函数
const Date* operator&() const // 实际上被编译器处理为: Date* operator&(const Date* this)
{
cout << "operator&() const" << endl;
return this;
}
};
int main()
{
const Date d4;
cout << &d4 << endl; // 打印输出对象d4的地址
return 0;
}
运行结果:
成功获取const修饰的变量d4的地址,并且程序确实是进入类中定义的取地址函数。
隐式const对象的取地址函数
例子程序:
#include <iostream>
using namespace std;
class Date
{
};
int main()
{
const Date d5;
cout << &d5 << endl; // 打印输出对象d5的地址
return 0;
}
运行结果:
我们发现,使用编译器自行生成的对于 const修饰的 对象取地址函数,依旧能够获取对象d5的地址。
总结:
对于这两个取地址的默认成员函数,可以不必过于费心。一般情况下,使用编译器自行生成的默认成员函数即可实现取对象地址的功能。