C++基础精讲-07

文章目录

  • 1. const对象
  • 2. 指向对象的指针
  • 3. 对象数组
  • 4. c++中const常见用法总结
    • 4.1 修饰常量
    • 4.2 修饰指针
    • 4.3 修饰函数参数
    • 4.4 修饰函数返回值
    • 4.5 修饰成员函数
    • 4.6 const对象
  • 5. 赋值运算符函数(补充)
    • 5.1 概念
    • 5.2 默认赋值运算符函数局限
    • 5.3 解决办法


1. const对象

1.概念:在 C++ 中,const 用于定义常量对象,一旦对象被初始化,其值就不能再改变。因为 const 对象只能被创建、撤销和只读访问,写操作是不允许的。
2.基本使用

#include <iostream>
#include <cstring>namespace myspace1
{using std::endl;using std::cout;using std::cin;using std::string;
}using namespace myspace1;class Person
{
public:Person() :_age(0), _name(new char[strlen("张三") + 1]){cout << "无参构造" << endl;strcpy(_name, "张三");}Person(int age,  const char* name) :_age(age), _name(new char[strlen(name) + 1]){cout << "有参构造" << endl;strcpy(_name, name);}void setAge(int age){_age = age;}void print(){cout << "age=" << _age << endl;cout << "name=" << _name << endl;}//const成员函数,不能修改对象的数据成员void print() const{cout << "age=" << _age << endl;cout << "name=" << _name << endl;}~Person(){delete[] _name;cout << "析构函数" << endl;}
private:int _age;char* _name;
};int main(void)
{//普通对象Person p1(1,"张大");p1.print();p1.setAge(10);p1.print();//const 对象只能被创建、撤销和只读访问,写操作是不允许的const Person p2(2, "张二");//p2.setAge(20)   //不允许//p2.print();     //不允许p2.print();        //调用的是void print() constreturn 0;
}

在这里插入图片描述

3.总结
const对象与const成员函数的规则:
(1)当类中有const成员函数和非const成员函数重载时,const对象会调用const成员函数,非const对象会调用非const成员函数;
(2)当类中只有一个const成员函数时,无论const对象还是非const对象都可以调用这个版本;
(3)当类中只有一个非const成员函数时,const对象就不能调用非const版本。

建议:如果一个成员函数中确定不会修改数据成员,就把它定义为const成员函数。

2. 指向对象的指针

int main(void)
{//在栈上开辟空间Person p1(1, "一号");Person* p2 = &p1;  //指向p1的指针;Person* p3 = nullptr;  //空指针;//在堆上开辟空间Person* p4 = new Person(2, "二号");delete p4;p4 = nullptr;Person* p5 = new Person();p5->setAge(20);  p5->print();(*p5).setAge(30);(*p5).print();delete p5;p5 = nullptr;return 0;
}

3. 对象数组

#include <iostream>
#include <cstring>namespace myspace1
{using std::endl;using std::cout;using std::cin;using std::string;
}using namespace myspace1;class Person
{
public:Person() :_age(0), _name(new char[strlen("张三") + 1]){cout << "无参构造" << endl;strcpy(_name, "张三");}Person(int age,  const char* name) :_age(age), _name(new char[strlen(name) + 1]){cout << "有参构造" << endl;strcpy(_name, name);}void setAge(int age){_age = age;}void print(){cout << "age=" << _age << endl;cout << "name=" << _name << endl;}//const成员函数,不能修改对象的数据成员void print() const{cout << "age=" << _age << endl;cout << "name=" << _name << endl;}~Person(){delete[] _name;cout << "析构函数" << endl;}
public:int _age;char* _name;
};int main(void)
{// 方式一:使用无参/默认构造函数创建对象数组cout << "使用默认构造函数创建对象数组:" << endl;Person p1[3];for (int i = 0; i < 3; i++) {p1[i].print();}// 方式二:使用有参构造函数初始化对象数组cout << "使用有参构造函数初始化对象数组:" << endl;Person p2[3] = {Person(20, "李四"),Person(21, "王五"),Person(22, "赵六")};for (int i = 0; i < 3; ++i) {p2[i].print();}// 方式三:动态分配对象数组cout << "动态分配对象数组:" << endl;Person* p3 = new Person[2];p3[0].setAge(25);strcpy(p3[0]._name, "孙七");p3[1].setAge(26);strcpy(p3[1]._name, "周八");for (int i = 0; i < 2; ++i) {p3[i].print();}// 释放动态分配的内存delete[] p3;return 0;
}

4. c++中const常见用法总结

4.1 修饰常量

//只有读权限,没有写权限
const int a = 10;    //==int const a=10;
//a = 20;  不可以修改

4.2 修饰指针

int main(void)
{int a = 10;int b = 20;//1.指向常量的指针const int* p1 = &a;  //==int const* p1 = &a;//*p1 = 100;   不可以为通过指针修改a的值;p1 = &b;  //可以修改指针p1的指向//2.常量指针;可以通过指针修改变量的值,但是指针的指向不可以变;int* const p2 = &a;*p2 = 20;//p2 = &b;  错误//3.指向常量的常量指针:指针的指向和所指向的值都不能改变const int* const p3 = &a;// *p3 = 20; // 错误,不能通过指针修改所指向的值// p3 = &b; // 错误,不能改变指针的指向return 0;
}

4.3 修饰函数参数

const 用于修饰函数参数时,能够保证在函数内部不会修改该参数的值。

void fun(const int a)
{//a = 100;   错误cout << "a=" <<a<< endl;
}int main(void)
{int a = 10;fun(a);return 0;
}

4.4 修饰函数返回值

const 修饰函数返回值时,表明返回值是一个常量,不能被修改。

//const 修饰函数返回值时,表明返回值是一个常量,不能被修改。
int const fun()
{int a = 10;return a;           
}int main(void)
{//fun() = 100;  错误,不可以修改return 0;
}

4.5 修饰成员函数

const 修饰类的成员函数时,表明该成员函数不会修改对象的状态

class MyClass {
private:int _value;
public:MyClass(int val) : _value(val) {}// 常量成员函数int getValue() const {// value = 20; // 错误,不能在常量成员函数中修改成员变量return _value;}
};

4.6 const对象

const 对象是指那些一经创建,其状态(即成员变量的值)就不能被修改的对象

class MyClass {
public:int _value;MyClass(int v) : _value(v) {}
};int main() {const MyClass obj(10);// obj.value = 20; // 错误,不能修改 const 对象的成员变量cout << "Value: " << obj._value << endl;return 0;
}

5. 赋值运算符函数(补充)

5.1 概念

Point pt1(1, 2), pt2(3, 4);
pt1 = pt2;//赋值操作

在执行 pt1 = pt2; 该语句时, pt1 与 pt2 都存在,所以不存在对象的构造,这要与 Point pt2 =pt1; 语句区分开,这是不同的。所以当 = 作用于对象时,需要调用的是赋值运算符函数
格式:

类名& operator=(const 类名 &)
class Point {
private:int _ix;int _iy;
public:Point(int x = 0, int y = 0) : _ix(x), _iy(y) {}//默认赋值运算符重载函数Point& operator=(const Point& rhs){_ix = rhs._ix;_iy = rhs._iy;return *this;}void print() const {cout << "(" << _ix << ", " << _iy << ")" <<endl;}
};int main() {Point p1(1, 2);Point p2(3, 4);p2 = p1;p2.print();return 0;
}

5.2 默认赋值运算符函数局限

(1)当类中包含指针数据成员且该指针指向堆上分配的内存时,默认的赋值运算符函数(编译器自动生成的)就无法满足需求,这可能会导致浅拷贝问题,进而引发内存泄漏或悬空指针等错误。
(2)默认的赋值运算符执行的是浅拷贝,也就是只复制指针的值,而不复制指针所指向的内存。这会造成两个对象的指针成员指向同一块内存,当其中一个对象被销毁时,它会释放这块内存,而另一个对象的指针就会变成悬空指针。另外,如果两个对象都尝试释放同一块内存,会导致重复释放,引发未定义行为。

class Person
{
public://构造函数Person(const int* age) :_age(new int(*age)){cout << "构造函数" << endl;}//打印函数void print()const{cout << "age=" << *_age << endl;}//析构函数~Person(){delete _age;cout << "析构函数" << endl;}
private:int* _age;
};int main(void)
{int age1 = 20;int age2 = 30;Person p1(&age1);p1.print();Person p2(&age2);p2.print();p2 = p1;return 0;
}

问题分析:
当执行 p2 = p1; 时,会调用编译器自动生成的默认赋值运算符。默认赋值运算符执行的是浅拷贝,即只复制指针的值,而不复制指针所指向的内存。这会导致 p1 和 p2 的 _age 指针指向同一块内存。由于浅拷贝,当 p2 对象被销毁时,会释放 _age 所指向的内存。此时,p1 的 _age 指针就会变成悬空指针。当 p1 对象被销毁时,再次释放同一块内存,会导致重复释放,引发未定义行为。

5.3 解决办法

为了解决浅拷贝的问题,需要自定义赋值运算符函数,实现深拷贝。深拷贝会为新对象分配新的内存,并将原对象的数据复制到新的内存中。

class Person
{
public://构造函数Person(const int* age) :_age(new int(*age)){cout << "构造函数" << endl;}//打印函数void print()const{cout << "age=" << *_age << endl;}//自定义赋值运算符函数Person& operator=(const Person& rhs){//自我赋值检查if (this != &rhs) {//释放当前_age空间;delete _age;_age = nullptr;_age = new int(*rhs._age);}return *this;}//析构函数~Person(){delete _age;cout << "析构函数" << endl;}
private:int* _age;
};int main(void)
{int age1 = 20;int age2 = 30;Person p1(&age1);p1.print();Person p2(&age2);p2.print();p2 = p1;return 0;
}

若你有自行定义某些特殊成员函数,编译器会为类自动生成以下 6 个默认函数:

  1. 默认构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值运算符
  5. 移动构造函数(C++11 及以后)
  6. 移动赋值运算符(C++11 及以后)

注意拷贝构造函数、赋值运算符函数、析构函数,如果需要手动定义其中的一个,那么另外两个也需要手动定义。 三合成原则

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76933.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试之接口测试用例设计

1.接口测试用例设计简介 我们对系统的需求分析完成之后&#xff0c;即可设计对应的接口测试用例&#xff0c;然后用接口测试用例进行接口测试。接口测试用例的设计也需要用到黑盒测试方法&#xff0c;其与功能测试用例设计的方法类似&#xff0c;接口测试用例设计中还需要增加…

(2)VTK C++开发示例 --- 绘制多面锥体

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 VTK C开发示例程序&#xff1b; 使用C 和VTK绘制一个多面锥体。 环境说明系统ubuntu22.04、windows11cmake3.22、3.2…

公司内部自建知识共享的方式分类、详细步骤及表格总结,分为开源(对外公开)和闭源(仅限内部),以及公共(全员可访问)和内部(特定团队/项目组)四个维度

以下是公司内部自建知识共享的方式分类、详细步骤及表格总结&#xff0c;分为开源&#xff08;对外公开&#xff09;和闭源&#xff08;仅限内部&#xff09;&#xff0c;以及公共&#xff08;全员可访问&#xff09;和内部&#xff08;特定团队/项目组&#xff09;四个维度&am…

DeepSeek使用001:Word中配置DeepSeek AI的V3和R1模型

文章目录 Word中配置DeepSeek大模型1、勾选开发工具2、信任中心设置3、添加DeepSeek-V3模型4、获取API KEY5、添加DeepSeek-R1模型6、新建组7、测试使用 Word中配置DeepSeek大模型 1、勾选开发工具 打开【选项】 选择【自定义功能区】 2、信任中心设置 打开【信任中心】&…

Spark-SQL核心编程语言

利用IDEA开发spark-SQL 创建spark-SQL测试代码 自定义函数UDF 自定义聚合函数UDAF 强类型的 Dataset 和弱类型的 DataFrame 都提供了相关的聚合函数&#xff0c; 如 count()&#xff0c; countDistinct()&#xff0c;avg()&#xff0c;max()&#xff0c;min()。除此之外&…

从图像“看出动作”

&#x1f4d8; 第一部分&#xff1a;运动估计&#xff08;Motion Estimation&#xff09; &#x1f9e0; 什么是运动估计&#xff1f; 简单说&#xff1a; &#x1f449; 给你一段视频&#xff0c;计算机要“看懂”里面什么东西动了、往哪动了、有多快。 比如&#xff1a; 一…

Spring Boot 使用 SMB 协议

2025/4/14 向全栈工程师迈进&#xff01; 一、详述SMB协议 SMB&#xff08;Server Message Block&#xff09;协议是一个网络文件共享协议&#xff0c;它使得计算机可以在网络中共享文件、打印机以及其他资源。SMB 主要用于 Windows 操作系统&#xff0c;但也有其他平台&#…

Spring编程式事务(本地事务)

使用 TransactionTemplate等类和 API 手动管理事务&#xff0c;控制事务的新建、提交、回滚等过程 方式一&#xff1a;使用 TransactionTemplate&#xff08;推荐方式&#xff09; Service public class OrderService {private final TransactionTemplate transactionTemplat…

itext7 html2pdf 将html文本转为pdf

1、将html转为pdf需求分析 经常会看到爬虫有这样的需求&#xff0c;将某一个网站上的数据&#xff0c;获取到了以后&#xff0c;进行分析&#xff0c;然后将需要的数据进行存储&#xff0c;也有将html转为pdf进行存储&#xff0c;作为原始存档&#xff0c;当然这里看具体的需求…

企业级低代码平台的架构范式转型研究

在快速迭代的数字时代&#xff0c;低代码平台如同一股清流&#xff0c;悄然成为开发者们的新宠。 它利用直观易用的拖拽式界面和丰富的预制组件&#xff0c;将应用程序的开发过程简化到了前所未有的程度。通过封装复杂的编程逻辑和提供强大的集成能力&#xff0c;低代码平台让…

C++ | STL之list详解:双向链表的灵活操作与高效实践

引言 std::list 是C STL中基于双向链表实现的顺序容器&#xff0c;擅长高效插入和删除操作&#xff0c;尤其适用于频繁修改中间元素的场景。与std::vector不同&#xff0c;std::list的内存非连续&#xff0c;但提供了稳定的迭代器和灵活的元素管理。本文将全面解析std::list的…

AI运算服务器工控机特点与应用

AI运算服务器工控机是专门针对工业环境设计的计算设备&#xff0c;结合了传统工控机&#xff08;工业控制计算机&#xff09;的可靠性与AI服务器的强大算力&#xff0c;广泛应用于智能制造、边缘计算、机器视觉、自动化控制等领域。以下是其核心特点、应用场景及选型建议&#…

25/4/9 算法笔记 DBGAN+强化学习+迁移学习实现青光眼图像去模糊1

整体实验介绍 实验主要是结合DBGAN对抗网络强化学习增强迁移学习增强实现青光眼图像去模糊。今天则是先完成了DBGAN板块模型的训练。 实验背景介绍 青光眼的主要特征有&#xff1a; 视盘形态与杯盘比CDR&#xff1a;青光眼患者主要表现为视杯扩大&#xff0c;盘沿变窄。 视…

智能复盘自动化系统搭建指南—基于DeepSeek API与Apple日历的整合实践

一、系统架构设计 本方案通过iOS快捷指令实现日历数据与AI分析的自动化交互&#xff0c;核心流程包含&#xff1a; 日历事件管理 创建每日循环的"AI复盘"日历事项实现当日备注信息的动态更新 数据处理模块时间日志标准化处理多维度数据特征提取 AI交互层对接DeepSeek…

01 位运算

12days 章节结构 00 算法前导课-编程基础&#xff08;自学的视频&#xff09; 01 位运算的奇巧淫技 02 查找与排序&#xff08;上&#xff09; 03 数组、查找与排序(下) 04 多维数组与矩阵 05 字符串专题 06 基本数学问题 06 递归、DFS、剪枝、回溯等问题 07 贪心策…

HDFS Full Block Report超限导致性能下降的原因分析

文章目录 前言发现问题失败的为什么是FBR块汇报频率的变化为什么FBR会反复失败HDFS性能下降导致Yarn负载变高的形式化分析理解线程理解IO Wait理解HDFS性能下降导致Yarn负载和使用率增高 引用 前言 我们的Yarn Cluster主要用来运行一批由Airflow定时调度的Spark Job&#xff0…

【Kubernetes基础--Pod深入理解】--查阅笔记2

深入理解Pod 为什么要有个Pod1. 容器协作与资源共享2. 简化调度和资源管理3. 设计模式支持 Pod 基本用法Pod 容器共享 VolumePod 的配置管理ConfigMap 概述创建 ConfigMap 资源对象在 Pod 中使用 ConfigMap使用 ConfigMap 的限制条件 为什么要有个Pod Pod 的引入并非技术冗余&…

Margin和Padding在WPF和CSS中的不同

CSS和WPF中 margin 与 padding 在方向上的规定基本一致&#xff0c;但在使用场景和一些细节上有所不同。 CSS - 方向规定&#xff1a; margin 和 padding 属性可以分别指定上、右、下、左四个方向的值。例如 margin:10px 20px 30px 40px; 表示上外边距为10px、右外边距为20…

gravity`(控制 View 内部内容的对齐方式)

文章目录 **1. 常用取值****示例** **2. layout_gravity&#xff08;控制 View 在父容器中的对齐方式&#xff09;****常用取值****示例** **3. gravity vs layout_gravity 对比****4. 注意事项****5. 总结** 作用对象&#xff1a;当前 View 的内部内容&#xff08;如 TextView…

Go:使用共享变量实现并发

竞态 在串行程序中&#xff0c;步骤执行顺序由程序逻辑决定&#xff1b;而在有多个 goroutine 的并发程序中&#xff0c;不同 goroutine 的事件先后顺序不确定&#xff0c;若无法确定两个事件先后&#xff0c;它们就是并发的。若一个函数在并发调用时能正确工作&#xff0c;称…