【C++】C++类与对象3:const成员函数与取地址运算符重载,权限管理的艺术

📌 相关专栏

很高兴你点开这篇文章✨

这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀

👍 点赞 ⭐ 收藏 💬 评论


文章目录

  • 前言
  • 一、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;
}

🐶 🐾 ✨ 🐾 🐶


🐾 下一篇我们继续学习:

  • 友元
  • 内部类
  • 匿名对象
  • 编译器优化

谢谢你看到这里呀

如果喜欢这篇内容,点个关注,下次更新不迷路✨

👍 点赞 ⭐ 收藏 💬 评论

相关推荐
影寂ldy1 小时前
C# 类和对象
开发语言·c#
丷丩1 小时前
MapLibre GL JS第25课:添加栅格瓦片源
开发语言·javascript·gis·mapbox·maplibre gl js
QiLinkOS1 小时前
【用呼吸重构创造价值关系——QiLink生态】
c语言·数据结构·c++·人工智能·单片机·嵌入式硬件·算法
朔北之忘 Clancy1 小时前
2026 年 3 月青少年软编等考 C 语言二级真题解析
c语言·开发语言·c++·学习·青少年编程·题解·考级
晚风予卿云月2 小时前
【前缀和】一维前缀和 & 二维前缀和
数据结构·c++·算法
Old Uncle Tom2 小时前
Harness Engineering 综述
java·开发语言·数据库
星原望野2 小时前
JAVA:策略模式的实战使用
java·开发语言·策略模式
码界筑梦坊2 小时前
282-基于Python的豆瓣音乐可视化分析推荐系统
开发语言·python·信息可视化·数据分析·flask·vue
LJianK12 小时前
java多态
java·开发语言·python