当前位置: 首页 > article >正文

类和对象——const修饰的类的对象和函数

const修饰的类的对象和函数

  • const成员函数和const对象
    • 1 const成员函数
    • 2 调用关系
    • 3 const在成员函数中的位置
    • 4 取地址&及const取地址操作符重载

const成员函数和const对象

1 const成员函数

const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
请添加图片描述
例如:

#include<iostream>
class Date {
public:
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	//编译器认为两个Print构成函数重载
	void Print() {
		using std::cout;
		using std::endl;
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Print() const {
		using std::cout;
		using std::endl;
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main() {
	Date d1(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	//编译器认为d2.Print(&d2),&d2的类型是const Date*,
	//会为d2匹配void Print() const
	d2.Print();
	return 0;
}

编译器的原则:永远是最匹配的,没有就找最合适的。权限可以缩小,但不可以放大。所以d1调用的是Print(),而d2调用的是Print() const

如果只有Print() const,则d1调用的是Print() const。例如:

#include<iostream>

class Date {
public:
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const {
		using std::cout;
		using std::endl;
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

int main() {
	Date d1(2022, 1, 13);
	d1.Print();
	return 0;
}

2 调用关系

const对象和const成员函数,它们与非const的对象和成员函数之间的关系:

  1. const对象不可以调用非const成员函数。权限放大。

因为非const成员函数可通过this指针间接修改对象,所以const对象不能将自身具有常属性的地址上传给this指针,防止权限放大。

参考样例:

#include<iostream>
class A {
public:
	void f1() {
		using std::cout;
		cout << "void f1()\n";
	}
	void f2() const {
		using std::cout;
		cout << "void f2()\n";
	}
};

int main() {
	const A a;
	//a.f1();//不允许。
	a.f2();//允许,权限平移
	return 0;
}
  1. const对象可以调用const成员函数。权限缩小。
#include<iostream>
class A {
public:
	void f1() {
		using std::cout;
		cout << "void f1()\n";
	}
	void f2() const {
		using std::cout;
		cout << "void f2()\n";
	}
};

int main() {
	A a;
	a.f2();//允许
	return 0;
}
  1. const成员函数内不可以调用其它的非const成员函数。
    因为const成员函数的this具有常属性,将this的值上传给非const成员函数,因为非const的成员函数不具有常属性的this,所以权限放大。
#include<iostream>
class A {
public:
	void f1() {
		using std::cout;
		cout << "void f1()\n";
	}
	void f2() const {
		using std::cout;
		cout << "void f2()\n";
		//f1();//不允许,权限放大
	}
};

在存在大量代码复用的类中需要特别注意。

  1. const成员函数内可以调用其它的const成员函数。权限缩小。
#include<iostream>
class A {
public:
	void f1() {
		using std::cout;
		cout << "void f1()\n";
		f2();//可以,权限缩小。
	}
	void f2() const {
		using std::cout;
		cout << "void f2()\n";
	}
};

3 const在成员函数中的位置

这是一个数组类:

#include<iostream>
#include<cstdlib>
class SeqList {
public:
	~SeqList() {
		free(a);
	}
	int& operator[](int i) const {//a向堆区申请内存,出了这里的作用域数据还在,可以传引用返回
		return a[i];
	}
private:
	//c++11允许给内置类型成员初始值,甚至是调用公共库函数
    //但如果函数执行出错无法反馈
	int* a = (int*)malloc(sizeof(int) * 10);
	size_t size = 0;
	size_t capacity = 10;
};

void print(const SeqList& a) {
	using std::cout;
	for (int i = 0; i < 10; i++) {
		cout << a[i] << ' ';
		a[i]++;//const只限制a本身的内容不可改变,
	}
	cout << "\n";
}

int main() {
	SeqList a;
	using std::cout;
	for (int i = 0; i < 10; i++)
		a[i] = i;//修改堆区的数据
	print(a);
	print(a);
	return 0;
}

函数int& operator[](int i) const{}虽然对this指针本身用const修饰,但是函数返回值是堆区某个位置的引用,所以[]并没有对堆区的数据进行限制。

数组类需要通过[]进行修改和访问数据,因此在部分场合我们想通过[]修改数据,在另一场合又希望这个数据无法被修改。

所以需要提供两个[]的重载,一个只读,一个可读可写,此时编译器会匹配最合适的[]。只读的[]重载一般需要用两个const进行限制。

#include<iostream>
#include<cstdlib>
class SeqList {
public:
	~SeqList() {
		free(a);
	}
	int& operator[](int i) {//对象可读可写
		return a[i];
	}
	const int& operator[](int i) const{//对象只读
		return a[i];
	}
private:
	int* a = (int*)malloc(sizeof(int) * 10);
	size_t size = 0;
	size_t capacity = 10;
};

void print(const SeqList& a) {
	using std::cout;
	for (int i = 0; i < 10; i++) {
		cout << a[i] << ' ';
		//a[i]++;//编译器为const SeqList匹配形参为const SeqList的operator[],所以不可修改
	}
	cout << "\n";
}

int main() {
	SeqList a;
	using std::cout;
	for (int i = 0; i < 10; i++)
		a[i] = i;//修改堆区的数据
	print(a);
	return 0;
}

还是需求问题,程序员想要用户不修改什么数据,就加const对类的成员函数进行限制。

4 取地址&及const取地址操作符重载

默认成员函数的取地址&重载一般不用重新定义 ,编译器默认会生成。其中用到了const修饰this时构成的重载。

class Date {
public:
	Date* operator&() {
		return this;
	}
	const Date* operator&() const {//参考上文的数组类
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想被别人取到对象的有效地址,于是给个假地址返回。

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif

#include<iostream>
using std::cout;
class Date {
public:
	Date(int year=1900,int month=0,int day=0){
		_year = year;
		_month = month;
		_day = day;
	}
	Date* operator&() {
		return nullptr;
	}
	const Date* operator&() const {
		return nullptr;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

int main() {
	Date a;
	const Date b;
	cout << (&a) << '\n' << (&b);
	return 0;
}

http://www.kler.cn/a/565057.html

相关文章:

  • DavGo简单部署WebDAV服务
  • 【Deepseek学习大模型】Roofline计算规则
  • FastAPI 学习笔记
  • C++ Qt常见面试题(4):Qt事件过滤器
  • AI如何通过大数据分析提升制造效率和决策智能化
  • Android SDK与NDK的区别
  • 【NLP 37、激活函数 ③ relu激活函数】
  • [Java基础] JVM常量池介绍(BeanUtils.copyProperties(source, target)中的属性值引用的是同一个对象吗)
  • JavaScript 简单类型与复杂类型-复杂类型的内存分配
  • Redis中SDS的数据结构
  • SpringBoot 日志文件相关 门面模式
  • ONNX转RKNN的环境搭建
  • 【Day48 LeetCode】图论问题 Ⅵ
  • Vue-Flow绘制流程图(Vue3+ElementPlus+TS)简单案例
  • 【C++教程】布尔类型
  • python量化交易——金融数据管理最佳实践——qteasy创建本地数据源
  • 8.Dashboard的导入导出
  • 打破关节动力桎梏!杭州宇树科技如何用“一体化设计”重塑四足机器人性能?
  • MFC获取所有硬件厂商和序列号
  • 如何搭建和管理 FTP 服务器