C++类与对象深度解析(一):从引用、内联函数到构造析构的编程实践

目录

一.引用

引用的特征:1.引用必须初始化

2.本质是别名

3.函数参数传递

4.常引用

5.函数返回值

6.权限 放大 缩小 平移

引用 vs 指针

二.内联函数

关键点说明

三.宏函数

四.类

什么是类?

简单的类

五.构造函数与析构函数

1. 构造函数(Constructor)

使用环境:

2. 析构函数(Destructor)

作用

示例

总结

六.this指针

1. 什么是 this 指针?

2. 为什么需要 this 指针?

3. 注意事项

七.拷贝构造函数

一、什么是拷贝构造函数?

二、浅拷贝(Shallow Copy)

什么是浅拷贝?

浅拷贝的问题

三、深拷贝(Deep Copy)

什么是深拷贝?

深拷贝解决方案

四、关键总结


一.引用

引用:引用不是新定义一个量而是给已存变量取一个别名,它和它引用的变量共用一块空间

引用的特征:

1.引用必须初始化

引用必须在声明时初始化,且不能重新绑定到其他对象。

int x = 10;
int &ref = x;  // 正确:引用必须初始化
// int &ref2;  // 错误:未初始化的引用
2.本质是别名

引用是变量的另一个名字,没有独立的内存地址,操作引用等价于操作原变量。

int x = 10;
int &ref = x;  // 正确:引用必须初始化
// int &ref2;  // 错误:未初始化的引用
3.函数参数传递

引用常用于函数参数,避免拷贝开销且允许修改实参(替代指针的简洁语法)。

void swap(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a = 1, b = 2;
    swap(a, b);  // 直接传递变量,无需取地址
    cout << a << " " << b;  // 输出 2 1
}
4.常引用

常引用可以绑定到临时对象或字面量,且不能通过引用修改原对象

const int &r1 = 42;  // 正确:常引用可绑定到字面量
int x = 10;
const int &r2 = x;   // 正确:r2 是 x 的只读别名
// r2 = 20;          // 错误:不能通过常引用修改值
5.函数返回值

引用可以作为函数返回值,但需确保返回的对象生命周期有效(避免悬空引用)。

int global = 100;

int& getGlobal() {
    return global;  // 返回全局变量的引用(安全)
}

int& dangerous() {
    int local = 50;
    return local;   // 错误:返回局部变量的引用(悬空引用)
}
6.权限 放大 缩小 平移
int main()
{
	//权限不能放大
	//const int a = 0;
	//int& b = a;
	//可以只是一个拷贝
	const int c = 0;
	int d = c;
	//引用过程中权限可以平移或者缩小
	int x = 0;
	int& y = x;//权限的平移
	const int& z = x;//权限的缩小
	++x;//可以,因为上面是缩小了z的权限
	//不可以++z;

	double dd = 1.11;
	int ii = dd;
	//  不可以   int& rii = dd;
	const int& rii = dd;//临时变量具有常性
	return 0;
}
引用 vs 指针
特性引用指针
初始化必须初始化可以声明后赋值
重新绑定不能可以
空值不能为空可以为 nullptr
内存占用无独立内存地址有自己的内存地址
语法简洁性直接操作对象(无需 * 或 ->需要解引用操作符(* 或 ->

二.内联函数

内联函数建议编译器将函数体直接插入调用位置(类似宏展开),以减少函数调用的开销(压栈、跳转、返回等)。适用于短小且频繁调用的函数。

#include <iostream>

// 1. 使用 inline 关键字声明内联函数
inline int add(int a, int b) {
    return a + b;
}

// 2. 类内定义的成员函数默认是内联的
class Calculator {
public:
    int multiply(int a, int b) {  // 隐式内联
        return a * b;
    }

    inline int subtract(int a, int b) {  // 显式内联
        return a - b;
    }
};

int main() {
    int x = 5, y = 3;

    // 调用内联函数 add
    std::cout << "加法结果: " << add(x, y) << std::endl;  // 编译器可能直接替换为 x + y

    Calculator calc;
    // 调用类内联函数
    std::cout << "乘法结果: " << calc.multiply(x, y) << std::endl;
    std::cout << "减法结果: " << calc.subtract(x, y) << std::endl;

    return 0;
}
关键点说明
  1. inline 关键字
    用于建议编译器将函数内联,但最终是否内联由编译器决定(可通过编译器优化选项控制,如 -O2)。

  2. 类成员函数的内联性

    • 在类内部直接定义的成员函数(如 multiply)默认是内联的。
    • 也可以在类外定义成员函数时使用 inline 关键字(需在头文件中实现)。
  3. 适用场景

    • 函数体短小(如 1-3 行代码)。
    • 频繁调用且对性能敏感的场景(如循环中的操作)。
  4. 注意事项

    • 避免内联复杂函数(如递归函数或包含循环的函数)。
    • 内联函数定义必须对调用者可见,通常直接写在头文件中。

三.宏函数

 优点-- 不需要建立栈帧,提高调用效率
 缺点-- 复杂,容易出错、可读性差、不能调试

举例三个问题的宏函数

Add(a | b, a & b); // (a | b + a & b)

为例:
以下三个有问题:

#define Add(int x, int y) return (x+y);
#define Add(x, y) x+y
#define Add(x, y) (x+y)

原因:(+ 与- 的优先级大于| 与&)

成功示范:
 

#define Add(x, y) ((x)+(y))

四.类

什么是类?
  • 是对象的“蓝图”或“模板”,用于定义对象的属性(成员变量)行为(成员函数)
  • 通过类可以创建具体的对象(实例),每个对象拥有独立的属性值。
  • 类的核心思想是封装:将数据和对数据的操作绑定在一起,并控制外部对数据的访问权限。

简单的类
#include <iostream>
using namespace std;

// 定义一个 Person 类
class Person {
public: // 公有成员,外部可以直接访问
    char name;  // 成员变量:姓名
    int age;      // 成员变量:年龄

    // 成员函数:显示信息
    void display() {
        cout << "姓名: " << name << ", 年龄: " << age << endl;
    }
};

int main() {
    // 创建 Person 类的对象
    Person person1;

    // 设置对象的属性
    person1.name = "张三";
    person1.age = 25;

    // 调用对象的方法
    person1.display(); // 输出:姓名: 张三, 年龄: 25

    return 0;
}

封装:通过 private 隐藏内部细节,通过 public 提供安全接口

五.构造函数与析构函数

1. 构造函数(Constructor)

作用

  • 在对象创建时自动调用,用于初始化对象的成员变量。
  • 可以重载(定义多个不同参数的构造函数)。
  • 没有返回类型,且名称与类名相同

默认构造函数:无参构造函数,全缺省构造函数,编译器默认生成的构造函数

示例

构造函数:

class Person {
public:
    char name;
    int age;

    // 默认构造函数(无参数)
    Person() {
        name = "Unknown";
        age = 0;
        cout << "默认构造函数被调用" << endl;
    }

    // 带参数的构造函数
    Person(string n, int a) {
        name = n;
        age = a;
        cout << "带参数的构造函数被调用" << endl;
    }
};
使用环境:
int main() {
    Person p1;                // 调用默认构造函数
    Person p2("Alice", 25);   // 调用带参数的构造函数
    return 0;
}
2. 析构函数(Destructor)
作用
  • 在对象销毁时自动调用,用于释放对象占用的资源(如内存、文件句柄)。
  • 名称是类名前加 ~没有参数和返回类型
  • 不能重载(每个类只有一个析构函数)。
示例

析构函数:

class FileHandler {
private:
    FILE* file;
public:
    // 构造函数:打开文件
    FileHandler(const char* filename) {
        file = fopen(filename, "r");
        cout << "文件已打开" << endl;
    }

    // 析构函数:关闭文件
    ~FileHandler() {
        if (file) {
            fclose(file);
            cout << "文件已关闭" << endl;
        }
    }
};

使用场景:
 

int main() {
    Person p1;                // 调用默认构造函数
    Person p2("Alice", 25);   // 调用带参数的构造函数
    return 0;
}
总结
特性构造函数析构函数
调用时机对象创建时对象销毁时
主要用途初始化成员变量释放资源(如内存、文件)
重载支持重载(多个构造函数)不支持重载(唯一析构函数)
默认生成未定义时编译器生成默认构造函数未定义时编译器生成默认析构函数

通过合理使用构造函数和析构函数,可以确保对象在生命周期内正确初始化和清理资源,避免内存泄漏和逻辑错误。

六.this指针

1. 什么是 this 指针?

在 C++ 中,this 是一个指向当前对象的指针

  • 每个类的非静态成员函数(包括构造函数和析构函数)内部都可以访问 this
  • this 指针是隐式存在的,不需要手动定义。
  • 它的类型是 ClassName*(例如,Person*Car*)。
class Person {
public:
    string name;
    void printName() {
        // 实际上等价于:cout << this->name << endl;
        cout << name << endl;
    }
};

2. 为什么需要 this 指针?

当你在类的成员函数中访问成员变量或调用成员函数时,编译器实际上是通过 this 指针来找到当前对象的成员
例如:

class Person {
public:
    string name;
    void printName() {
        // 实际上等价于:cout << this->name << endl;
        cout << name << endl;
    }
};

3. 注意事项

this 是一个指针静态函数属于类,而不是对象,因此不能使用 this。this指针是形参所以是存在栈中的

class MyClass {
public:
    static void staticFunc() {
        // this->xxx;  // 错误!静态函数没有 this
    }
};

七.拷贝构造函数

一、什么是拷贝构造函数?

拷贝构造函数是一个特殊的构造函数,用于通过已存在的对象创建一个新对象。当发生以下情况时会自动调用:

  1. 用已有对象初始化新对象
  2. 对象作为函数参数传递(值传递)
  3. 对象作为函数返回值(值返回)

基本语法:

ClassName(const ClassName& other);
二、浅拷贝(Shallow Copy)
什么是浅拷贝?
  • 默认的拷贝构造函数是浅拷贝
  • 直接复制成员变量的值(包括指针地址)
  • 如果类中有指针成员,会导致多个对象指向同一块内存
浅拷贝的问题
class ShallowCopy {
public:
    int* data;
    //构造函数
    ShallowCopy(int val) {
        data = new int(val); // 动态分配内存
    }
    
    // 默认拷贝构造函数是浅拷贝:ShallowCopy(const ShallowCopy& other) = default;

     //析构函数
    
    ~ShallowCopy() {
        delete data; // 释放内存
    }
};

int main() {
    ShallowCopy obj1(10);
    ShallowCopy obj2 = obj1; // 浅拷贝
    
    // 问题:obj1和obj2的data指向同一块内存
    // 当main结束时,obj2先调用析构函数释放内存
    // 然后obj1的析构函数会尝试释放已释放的内存 → 程序崩溃!
}
三、深拷贝(Deep Copy)
什么是深拷贝?
  • 需要手动实现拷贝构造函数
  • 为指针成员分配新的内存
  • 复制指针指向的内容,而不是复制指针地址
深拷贝解决方案
class DeepCopy {
public:
    int* data;
    
    DeepCopy(int val) {
        data = new int(val);
    }
    
    // 手动实现深拷贝构造函数
    DeepCopy(const DeepCopy& other) {
        data = new int(*other.data); // 分配新内存并复制值
    }
    
    // 注意:还需要重载赋值运算符(规则同理)
    DeepCopy& operator=(const DeepCopy& other) {
        if (this != &other) {
            delete data;            // 释放原有内存
            data = new int(*other.data); // 深拷贝
        }
        return *this;
    }
    
    ~DeepCopy() {
        delete data;
    }
};

int main() {
    DeepCopy obj1(20);
    DeepCopy obj2 = obj1; // 深拷贝
    
    // obj1.data 和 obj2.data 指向不同内存
    // 析构时不会出现重复释放问题
}
四、关键总结
浅拷贝深拷贝
复制内容复制指针地址复制指针指向的内容
内存安全性多个对象共享同一内存,易导致崩溃每个对象拥有独立内存,安全可靠
实现方式默认拷贝构造函数需要手动实现拷贝构造函数
适用场景无动态内存分配的简单类有指针成员或动态分配资源的类

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes:EKS 中 Istio Ingress Gateway 负载均衡器配置及常见问题解析

引言 在云原生时代&#xff0c;Kubernetes 已经成为容器编排的事实标准。AWS EKS (Elastic Kubernetes Service) 作为一项完全托管的 Kubernetes 服务&#xff0c;简化了在 AWS 上运行 Kubernetes 的复杂性。Istio 作为服务网格领域的佼佼者&#xff0c;为微服务提供了流量管理…

【CUDA 】第4章 全局内存——4.4 核函数可达到的带宽(3展开转置)【补图】

CUDA C编程笔记 第四章 全局内存4.4 核函数可达到的带宽4.4.2.3 展开转置【为每个线程分配更独立的任务】 待解决的问题&#xff1a; 第四章 全局内存 4.4 核函数可达到的带宽 4.4.2.3 展开转置【为每个线程分配更独立的任务】 展开&#xff1a;提高转置内存带宽的利用率&a…

后端重载和重写的区别

重载 相同的方法名&#xff0c;形参数量不同或者参数顺序不同或者参数类型不同称为方法重载 重写 方法名和形参列表相同 重写方法前提:必须存在继承关系 (1)方法重载是&#xff1a;一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法…

字节最新AI 版IDE:用Trae开发网站打包信息追踪插件,国产版Cursor表现如何?

文章首发地址&#xff1a;https://juejin.cn/post/7472684607365922850 插件背景及项目概述 在现代前端开发中&#xff0c;我们常常需要获取当前线上环境的代码构建信息&#xff0c;如项目打包人、打包时间、Git版本信息等。在持续集成/持续交付&#xff08;CI/CD&#xff09…

MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part 1

第01章_Linux下MySQL的安装与使用 首先在vmware中下载centos7&#xff0c;实际上8更好一点&#xff0c;不过centos已经是时代的眼泪了&#xff0c;我之前已经教过了&#xff0c;不过是忘了&#xff0c;所以重新说一遍&#xff0c;看文档即可 2.开机前修改mac地址 &#xff0…

网络工程师 (47)QOS

一、概念与原理 QOS即服务质量&#xff08;Quality of Service&#xff09;是一种网络技术&#xff0c;用于管理和保证网络中不同类型的质量和性能。它通过设置优先级和带宽限制等策略&#xff0c;确保关键应用&#xff08;如视频会议、语音通信&#xff09;的数据包能够在网络…

什么是幂等性?

一.幂等性 什么是幂等性&#xff1f; 在计算机科学和数学领域中&#xff0c;” 幂等性 “虽然源于相同的概念&#xff0c;但其应用和具体含义有所不同 在数学中&#xff1a;幂等性是一个代数性质&#xff0c;描述的是一个操作或函数在多次应用后结果不变的特性 在分布式系统…

PyCharm Terminal 自动切换至虚拟环境

PyCharm 虚拟环境配置完毕后&#xff0c;打开终端&#xff0c;没有跟随虚拟环境切换&#xff0c;如图所示&#xff1a; 此时&#xff0c;需要手动将终端切换为 Command Prompt 模式 于是&#xff0c;自动切换至虚拟环境 每次手动切换&#xff0c;比较麻烦&#xff0c;可以单…

YOLOv12从入门到入土(含结构图)

论文链接&#xff1a;https://arxiv.org/abs/2502.12524 代码链接&#xff1a;https://github.com/sunsmarterjie/yolov12 文章摘要&#xff1a; 长期以来&#xff0c;增强YOLO框架的网络架构一直至关重要&#xff0c;但一直专注于基于cnn的改进&#xff0c;尽管注意力机制在建…

我用AI做数据分析之数据清洗

我用AI做数据分析之数据清洗 AI与数据分析的融合效果怎样&#xff1f; 这里描述自己在使用AI进行数据分析&#xff08;数据清洗&#xff09;过程中的几个小故事&#xff1a; 1. 变量名的翻译 有一个项目是某医生自己收集的数据&#xff0c;变量名使用的是中文&#xff0c;分…

解锁机器学习核心算法 | K-平均:揭开K-平均算法的神秘面纱

一、引言 机器学习算法种类繁多&#xff0c;它们各自有着独特的优势和应用场景。前面我们学习了线性回归算法、逻辑回归算法、决策树算法。而今天&#xff0c;我们要深入探讨的是其中一种经典且广泛应用的聚类算法 —— K - 平均算法&#xff08;K-Means Algorithm&#xff09…

Bigemap Pro如何设置经纬网出图网格设置

第一步&#xff1a;打开bigemap pro软件&#xff0c;单击顶部网格选项第二栏&#xff0c;弹出经纬网设置对话框&#xff0c;如下图&#xff1a; 按作图需求自定义设置后&#xff0c;点击应用如下图&#xff1a; 第二步&#xff1a;设置好经纬网之后&#xff0c;进行作图&#x…

K8s 之端口暴露(The Port of K8s is Exposed)

K8s 之端口暴露 Kubernetes 是一个用于管理容器化应用程序的流行工具。然而&#xff0c;关于它的工作原理存在一些误解。最常见的误解之一是关于 Kubernetes Pod 中的端口暴露。本文将解释 Kubernetes 中端口暴露的真相。 1 误解 像许多 Kubernetes 新手一样&#xff0c;我最…

操作系统2.4

一、死锁&#xff0c;饥饿&#xff0c;死循环 死锁&#xff1a;各进程互相等待对方手里的资源&#xff0c;导致各进程都阻塞&#xff0c;无法向前推进的现象 饥饿&#xff1a;由于长期得不到想要的资源&#xff0c;某进程无法向前推进的现象&#xff0c;例如&#xff1a;短进…

解决DeepSeek服务器繁忙问题的实用指南

目录 简述 1. 关于服务器繁忙 1.1 服务器负载与资源限制 1.2 会话管理与连接机制 1.3 客户端配置与网络问题 2. 关于DeepSeek服务的备用选项 2.1 纳米AI搜索 2.2 硅基流动 2.3 秘塔AI搜索 2.4 字节跳动火山引擎 2.5 百度云千帆 2.6 英伟达NIM 2.7 Groq 2.8 Firew…

c++进阶———继承

1.引言 在一些大的项目中&#xff0c;我们可能要重复定义一些类&#xff0c;但是很麻烦&#xff0c;应该怎么办呢&#xff1f;举个简单的例子&#xff0c;我要做一个全校师生统计表&#xff0c;统计学号&#xff0c;教师编号&#xff0c;姓名&#xff0c;年龄&#xff0c;电话…

Android 平台GB28181设备接入实战指南

一、引言 随着视频监控技术的不断发展&#xff0c;国标 GB28181 协议在安防监控领域得到了广泛应用。该协议为不同厂家的视频监控设备之间的互联互通提供了统一的规范&#xff0c;使得设备的接入与管理变得更加简单和高效。在 Android 平台上实现 GB28181 设备接入&#xff0c…

细说Java 引用(强、软、弱、虚)和 GC 流程(一)

一、引用概览 1.1 引用简介 JDK1.2中引入了 Reference 抽象类及其子类&#xff0c;来满足不同场景的 JVM 垃圾回收工作&#xff1a; SoftReference 内存不足&#xff0c;GC发生时&#xff0c;引用的对象&#xff08;没有强引用时&#xff09;会被清理&#xff1b;高速缓存使用…

基于图像处理的裂缝检测与特征提取

一、引言 裂缝检测是基础设施监测中至关重要的一项任务,尤其是在土木工程和建筑工程领域。随着自动化技术的发展,传统的人工巡检方法逐渐被基于图像分析的自动化检测系统所取代。通过计算机视觉和图像处理技术,能够高效、精确地提取裂缝的几何特征,如长度、宽度、方向、面…

android ViewPager 管理 Fragment的预加载onCreate

一、前言 当ViewPager 加载多个 Fragment时候&#xff0c;怎么管理Fragment预加载。因为有些数据需要提前加载&#xff0c;第一个方便后面数据使用&#xff0c;提前初始化。或者预加载网络数据等。 二、实现示例 在onCreate方法进行数据预加载。如果在onCreateView函数里面&…