const与取地址重载
- 前言
- 一、const
- 正常用法
- const成员函数
- 问题
- `const`对象可以调用非`const`成员函数吗
- 非`const`对象可以调用`const`成员函数吗
- `const`成员函数内可以调用其它的非`const`成员函数吗
- 非`const`成员函数内可以调用其它的`const`成员函数吗
- 总结
- 二、取地址及const取地址操作符重载
- 概念
- 示例
前言
类的6个默认成员函数:如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
class Date {};
一、const
正常用法
在C++中,可以使用const
关键字来声明一个常量成员。常量成员是指在类中声明的成员变量被标记为只读,即不能在类的方法中进行修改。常量成员的值在对象创建时被初始化,并且在对象的整个生命周期中保持不变。
常量成员的声明方式为在成员变量的类型前加上const
关键字。例如:
class MyClass {
public:
const int myConst = 10; // 常量成员的声明和初始化
};
在上述示例中,myConst
被声明为一个常量成员,其初始值为10。
const成员函数
除了上面的这种用法外,C++在类里定义了新的const
用法,将const
修饰的“成员函数”称之为const
成员函数,const
修饰类成员函数,实际修饰该成员函数隐含的this
指针,表明在该成员函数中不能对类的任何成员进行修改。
我们来看看下面的代码
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << "Print()" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
void Print() const
{
cout << "Print()const" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
void Test()
{
Date d1(2022, 1, 13);
d1.Print();
const Date d2(2022, 1, 13);
d2.Print();
}
问题
请思考下面的几个问题:
const
对象可以调用非const
成员函数吗
不可以。在C++中,一个对象如果被声明为const
,则表示该对象是只读的,其成员变量不能被修改。因此,一个const
对象只能调用其成员函数中被声明为const
的成员函数。
如果一个成员函数没有被声明为const
,则它默认是一个非const
成员函数。非const
成员函数可以修改对象的成员变量,因此不能被const
对象调用。
如果一个成员函数需要被const
对象调用,应该在函数声明的末尾加上const
关键字。这样声明的成员函数被称为const
成员函数,它保证不修改对象状态,并且可以被const
对象调用。
示例:
class MyClass {
public:
void nonConstFunc() {
// 可以修改对象状态的非const成员函数
}
void constFunc() const {
// 不能修改对象状态的const成员函数
}
};
int main() {
const MyClass obj;
obj.constFunc(); // 可以调用const成员函数
// obj.nonConstFunc(); // 错误,不能调用非const成员函数
return 0;
}
在上述示例中,constFunc()
被声明为const
成员函数,因此可以被const
对象调用。而nonConstFunc()
是非const
成员函数,因此不能被const
对象调用。
非const
对象可以调用const
成员函数吗
正确,非const
对象可以调用const
成员函数。const
成员函数的作用是表示该函数不会修改对象的状态。因此,无论是const
对象还是非const
对象,都可以调用不会修改对象状态的const
成员函数。
示例:
class MyClass {
public:
void nonConstFunc() {
// 可以修改对象状态的非const成员函数
}
void constFunc() const {
// 不能修改对象状态的const成员函数
}
};
int main() {
MyClass obj;
obj.constFunc(); // 可以调用const成员函数
obj.nonConstFunc(); // 也可以调用非const成员函数
return 0;
}
在上述示例中,constFunc()
是const成员函数,因此可以被非const
对象obj
调用。同时,非const
成员函数nonConstFunc()
也可以被非const
对象obj
调用。因为非const对象可以修改对象的状态,所以可以调用const
成员函数和非const
成员函数。
const
成员函数内可以调用其它的非const
成员函数吗
在const
成员函数内部,只能调用其他const
成员函数。const
成员函数的作用是保证不修改对象的状态,因此它们不能调用非const
成员函数。如果在const
成员函数内部调用非const
成员函数,编译器将会报错。
示例:
class MyClass {
public:
void nonConstFunc() {
// 可以修改对象状态的非const成员函数
}
void constFunc() const {
// 不能修改对象状态的const成员函数
nonConstFunc(); // 错误! const成员函数不能调用非const成员函数
}
};
int main() {
MyClass obj;
obj.constFunc();
return 0;
}
在上述示例中,constFunc()
是const成员函数,它尝试在内部调用了非const
成员函数nonConstFunc()
,这将导致编译错误。
如果确实需要在const
成员函数内部调用非const
成员函数,可以使用const_cast
进行类型转换,但是这样会绕过const
的限制,不推荐使用。
void constFunc() const {
const_cast<MyClass*>(this)->nonConstFunc(); // 可以调用非const成员函数,但不推荐使用
}
非const
成员函数内可以调用其它的const
成员函数吗
非const
成员函数可以调用其他的const
成员函数。在非const
成员函数内部,可以调用任何类型的成员函数,包括const
成员函数。这是因为非const
成员函数可以修改对象的状态,而const
成员函数不允许修改对象的状态,所以在非const
成员函数内部调用const
成员函数是安全的。
示例:
class MyClass {
public:
void nonConstFunc() {
// 可以修改对象状态的非const成员函数
constFunc(); // 可以调用const成员函数
}
void constFunc() const {
// 不能修改对象状态的const成员函数
}
};
int main() {
MyClass obj;
obj.nonConstFunc(); // 调用非const成员函数,它内部调用了const成员函数
return 0;
}
在上述示例中,nonConstFunc()
是非const
成员函数,它内部调用了const
成员函数constFunc()
,这是允许的。因为非const
成员函数可以修改对象的状态,包括调用const
成员函数不会破坏对象的const
属性。
总结
权限缩小可以,权限放大不可以,即被const
修饰的是可读的,不被修饰的是可读可写的,不被修饰的可以访问被修饰的
二、取地址及const取地址操作符重载
概念
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。 不想知道太多的读者可以直接跳过。
class Date
{
public:
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!
示例
在C++中,const
关键字用于修饰变量,表示该变量的值不可修改。const
修饰符同样也可以用于指针,表示指针所指向的内容是不可修改的。
在C++中,对于指针类型,可以重载取地址操作符(&
)来返回指针的地址。但是,const
修饰符的存在可能导致取地址操作符无法重载。
当一个指针被声明为const
类型时,取地址操作符不会返回指针的地址,而是返回指针指向的内容的地址。这是因为const
关键字修饰的指针表示其所指向的内容是不可修改的,因此不需要返回指针的地址。
以下是一个示例代码,展示了如何重载取地址操作符和处理const
修饰符:
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int value) : value(value) {}
int* operator&() {
std::cout << "Non-const operator&" << std::endl;
return &value;
}
const int* operator&() const {
std::cout << "Const operator&" << std::endl;
return &value;
}
};
int main() {
MyClass obj(10);
int* ptr1 = &obj; // 调用非const版本的operator&
const int* ptr2 = &obj; // 调用const版本的operator&
return 0;
}
输出结果为:
Non-const operator&
Const operator&
在上述代码中,类MyClass
重载了取地址操作符(&
)。它包含两个版本:一个是非const
版本,另一个是const
版本。
在main()
函数中,先后使用非const
指针和const
指针获取MyClass
对象的地址。当使用非const
指针时,调用非const
版本的operator&
;而当使用const
指针时,调用const
版本的operator&
。
通过重载取地址操作符,我们可以根据指针的类型选择合适的操作。对于const
修饰符,我们还可以使用const
版本的operator&
来获取指向内容的地址。