📌 相关专栏
-
【C++ 专栏】
📌 相关文章推荐
很高兴你点开这篇文章✨
这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀
👍 点赞 ⭐ 收藏 💬 评论
文章目录
- 前言
- 一、const成员函数
-
- [1.1 什么是const成员函数](#1.1 什么是const成员函数)
- [1.2 const对象的限制](#1.2 const对象的限制)
- [1.3 权限规则(回顾与深化)](#1.3 权限规则(回顾与深化))
- [1.4 构造函数为什么不能用const修饰?](#1.4 构造函数为什么不能用const修饰?)
- [1.5 const成员函数的重载](#1.5 const成员函数的重载)
- 二、取地址运算符重载
-
- [2.1 什么是取地址运算符重载](#2.1 什么是取地址运算符重载)
- [2.2 默认实现 vs 自定义实现](#2.2 默认实现 vs 自定义实现)
- [2.3 特殊场景:隐藏对象地址](#2.3 特殊场景:隐藏对象地址)
- [2.4 为什么需要const版本的取地址重载?](#2.4 为什么需要const版本的取地址重载?)
- 三、this指针的类型
- 四、常见面试题
-
- [🐾 </h3>Q1:const对象可以调用非const成员函数吗?](#🐾 Q1:const对象可以调用非const成员函数吗?)
- [🐾 </h3>Q2:普通对象可以调用const成员函数吗?](#🐾 Q2:普通对象可以调用const成员函数吗?)
- [🐾 </h3>Q3:取地址运算符重载什么时候会用?](#🐾 Q3:取地址运算符重载什么时候会用?)
- [🐾 </h3>Q4:不重载取地址运算符,const对象能取地址吗?](#🐾 Q4:不重载取地址运算符,const对象能取地址吗?)
- 五、知识点汇总
- 六、总结
- 七、本文所有代码
-
- [🐾 </h3>Date.h](#🐾 Date.h)
- [🐾 </h3>Date.cpp](#🐾 Date.cpp)
- [🐾 </h3>Test.cpp](#🐾 Test.cpp)
前言
在前几篇文章中,我们学习了构造函数、运算符重载、流插入等知识。这一篇我们来聊两个"小而精"的特性:
- const成员函数: 让对象承诺"我不会修改你"
- 取地址运算符重载: 控制别人获取对象地址的方式
💡这两个特性看似简单,但在实际工程中非常实用。理解它们,能让我们写出更安全、更规范的C++代码。
🐶 🐾 ✨ 🐾 🐶
一、const成员函数
1.1 什么是const成员函数
在成员函数的参数列表后面加上const关键字,表示这个函数不会修改对象的成员变量。
cpp
class Date
{
public:
void Print() const // const成员函数
{
// _year = 2011; // ❌ 报错!const函数不能修改成员变量
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetYear(int year) // 非const成员函数
{
_year = year; // ✅ 可以修改
}
private:
int _year;
int _month;
int _day;
};
1.2 const对象的限制
const对象只能调用const成员函数。
cpp
int main()
{
const Date d1; // const对象
d1.Print(); // ✅ Print是const函数,可以调用
// d1.SetYear(2026); // ❌ SetYear不是const函数,不能调用
Date d2; // 非const对象
d2.Print(); // ✅ 可以调用const函数
d2.SetYear(2026); // ✅ 可以调用非const函数
return 0;
}
1.3 权限规则(回顾与深化)
在第二篇文章中我们讲过引用的权限规则,const成员函数也是同样的道理:
第二篇文章的链接
| 场景 | 对象权限 | 函数权限 | 是否允许 | 权限 |
|---|---|---|---|---|
| 普通对象调用普通函数 | 可读可写 | 可读可写 | 允许 | 权限平移 |
| 普通对象调用const函数 | 可读可写 | 只读 | 允许 | 权限缩小 |
| const对象调用普通函数 | 只读 | 可读可写 | 不允许 | 权限放大(禁止) |
| const对象调用const函数 | 只读 | 只读 | 允许 | 权限平移 |
核心原则:权限只能缩小或平移,不能放大。
1.4 构造函数为什么不能用const修饰?
构造函数的作用是初始化对象,必然要修改成员变量。如果用const修饰,就无法给成员变量赋值了。
cpp
// ❌ 错误!构造函数不能用const
Date(int year, int month, int day) const
{
_year = year; // 编译错误
}
// ✅ 正确
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
1.5 const成员函数的重载
const可以参与函数重载:同名的const版本和非const版本可以共存。
cpp
class Date
{
public:
// 普通版本(可修改)
int& operator[](int pos)
{
return arr[pos];
}
// const版本(只读)
const int& operator[](int pos) const
{
return arr[pos];
}
};
编译器会根据对象是否是const来选择调用哪个版本。
🐶 🐾 ✨ 🐾 🐶
二、取地址运算符重载
2.1 什么是取地址运算符重载
C++允许重载一元运算符&(取地址运算符),控制获取对象地址的行为。
cpp
class Date
{
public:
// 普通对象的取地址
Date* operator&()
{
return this;
}
// const对象的取地址
const Date* operator&() const
{
return this;
}
};
2.2 默认实现 vs 自定义实现
| 实现方式 | 返回值 | 使用场景 |
|---|---|---|
| 默认(不重载) | 返回真实地址 | 绝大多数情况都适用 |
| 返回this | 返回真实地址 | 和默认行为一致 |
| 返回nullptr | 返回空指针 | 特殊场景:隐藏对象地址 |
| 返回其他地址 | 自定义地址 | 极少使用 |
2.3 特殊场景:隐藏对象地址
某些情况下,为了安全(比如防止别人通过地址修改对象),可以让取地址运算符返回nullptr:
cpp
class SecretDate
{
public:
Date* operator&()
{
return nullptr; // 返回空,隐藏真实地址
}
const Date* operator&() const
{
return nullptr;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
SecretDate d;
SecretDate* p = &d; // p = nullptr,获取不到真实地址
}
使用场景:
- 防止外部通过指针意外修改对象
- 调试时阻止他人获取敏感对象地址
- 某些单例模式中隐藏实例地址
2.4 为什么需要const版本的取地址重载?
如果只有普通版本的operator&(),const对象调用时会发生什么?
cpp
class Date
{
public:
Date* operator&() { return this; }
// 没有 const 版本
};
int main()
{
const Date d1;
const Date* p = &d1; // ❌ 编译错误!
}
-
原因: const对象调用成员函数,需要匹配const版本的函数。如果没有const版本,编译器无法将this指针(指向const对象)转换为Date*(指向非const),违反权限放大规则。
-
解决办法: 提供const版本的重载。
cpp
class Date
{
public:
Date* operator&() { return this; } // 普通对象
const Date* operator&() const { return this; } // const对象
};
🐶 🐾 ✨ 🐾 🐶
三、this指针的类型
有了const成员函数的概念,我们可以更清晰地理解this指针的类型:
| 成员函数类型 | this指针类型 |
|---|---|
| 普通成员函数 | ClassName* const this |
| const成员函数 | const ClassName* const this |
cpp
class Date
{
public:
// this 类型:Date* const this
void Print()
{
// this->_year = 2026; // ✅ 可以修改成员
}
// this 类型:const Date* const this
void PrintConst() const
{
// this->_year = 2026; // ❌ 不能修改成员
}
};
🐶 🐾 ✨ 🐾 🐶
四、常见面试题
🐾 Q1:const对象可以调用非const成员函数吗?
- 不能。非const成员函数可能修改对象,但const对象是只读的,调用会违反权限规则,编译报错。
🐾 Q2:普通对象可以调用const成员函数吗?
- 可以。这是权限缩小(可读可写 → 只读),是允许的。
🐾 Q3:取地址运算符重载什么时候会用?
- 很少用。特殊场景如:调试时隐藏对象地址、单例模式中禁止外部获取实例地址、防止意外修改。
🐾 Q4:不重载取地址运算符,const对象能取地址吗?
- 可以。编译器会生成默认版本,const对象返回const ClassName*,普通对象返回ClassName*。
🐶 🐾 ✨ 🐾 🐶
五、知识点汇总
| 知识点 | 核心要点 |
|---|---|
| const成员函数 | 函数体后加const,承诺不修改成员变量 |
| const对象 | 只能调用const成员函数 |
| 权限规则 | 只能缩小/平移,不能放大 |
| 构造函数 | 不能用const修饰(需要初始化成员) |
| 取地址重载 | operator&() 返回 this 或自定义值 |
| const取地址重载 | const对象需要const版本,保证权限正确 |
| 特殊用途 | 返回nullptr可隐藏对象真实地址 |
🐶 🐾 ✨ 🐾 🐶
六、总结
🐾 这一篇我们学习了:
- const成员函数:让函数承诺不修改对象,是C++安全编程的重要工具
- const对象:只能调用const成员函数,遵循权限规则
- 取地址运算符重载:控制对象地址的获取方式,特殊场景下可以返回nullptr
🐶 🐾 ✨ 🐾 🐶
七、本文所有代码
🐾 Date.h
cpp
#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 2010, int month = 1, int day = 1);
void Print() const; // const成员函数
bool CheckDate();
int GetMonthDay(int year, int month)
{
static int monthDayArr[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
{
return 29;
}
return monthDayArr[month];
}
// 取地址运算符重载
Date* operator&()
{
return this;
// return nullptr; // 特殊场景:隐藏地址
}
const Date* operator&() const
{
return this;
// return nullptr;
}
private:
int _year;
int _month;
int _day;
};
🐾 Date.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
bool Date::CheckDate()
{
if (_month < 1 || _month > 12 ||
_day < 1 || _day > GetMonthDay(_year, _month))
{
return false;
}
return true;
}
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "非法日期" << endl;
}
}
void Date::Print() const
{
// _year = 2011; // 报错:const函数不能修改成员
cout << _year << "-" << _month << "-" << _day << endl;
}
🐾 Test.cpp
cpp
#include"Date.h"
int main()
{
const Date d1; // const对象
d1.Print(); // ✅ 调用const版本
Date d2; // 普通对象
d2.Print(); // ✅ 也可以调用const版本
return 0;
}
🐶 🐾 ✨ 🐾 🐶
🐾 下一篇我们继续学习:
- 友元
- 内部类
- 匿名对象
- 编译器优化

谢谢你看到这里呀
如果喜欢这篇内容,点个关注,下次更新不迷路✨
👍 点赞 ⭐ 收藏 💬 评论
