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

目录

一.引用

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

2.本质是别名

3.函数参数传递

4.常引用

5.函数返回值

[6.权限 放大 缩小 平移](#6.权限 放大 缩小 平移)

[引用 vs 指针](#引用 vs 指针)

二.内联函数

关键点说明

三.宏函数

四.类

什么是类?

简单的类

五.构造函数与析构函数

[1. 构造函数(Constructor)](#1. 构造函数(Constructor))

使用环境:

[2. 析构函数(Destructor)](#2. 析构函数(Destructor))

作用

示例

总结

六.this指针

[1. 什么是 this 指针?](#1. 什么是 this 指针?)

[2. 为什么需要 this 指针?](#2. 为什么需要 this 指针?)

[3. 注意事项](#3. 注意事项)

七.拷贝构造函数

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

[二、浅拷贝(Shallow Copy)](#二、浅拷贝(Shallow Copy))

什么是浅拷贝?

浅拷贝的问题

[三、深拷贝(Deep Copy)](#三、深拷贝(Deep Copy))

什么是深拷贝?

深拷贝解决方案

四、关键总结


一.引用

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

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

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

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

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

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

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

cpp 复制代码
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.常引用

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

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

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

cpp 复制代码
int global = 100;

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

int& dangerous() {
    int local = 50;
    return local;   // 错误:返回局部变量的引用(悬空引用)
}
6.权限 放大 缩小 平移
cpp 复制代码
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
内存占用 无独立内存地址 有自己的内存地址
语法简洁性 直接操作对象(无需 *-> 需要解引用操作符(*->

二.内联函数

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

cpp 复制代码
#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. 注意事项

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

三.宏函数

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

举例三个问题的宏函数

cpp 复制代码
Add(a | b, a & b); // (a | b + a & b)

为例:

以下三个有问题:

cpp 复制代码
#define Add(int x, int y) return (x+y);
#define Add(x, y) x+y
#define Add(x, y) (x+y)

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

成功示范:

cpp 复制代码
#define Add(x, y) ((x)+(y))

四.类

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

作用

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

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

示例

构造函数:

cpp 复制代码
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;
    }
};
使用环境:
cpp 复制代码
int main() {
    Person p1;                // 调用默认构造函数
    Person p2("Alice", 25);   // 调用带参数的构造函数
    return 0;
}
2. 析构函数(Destructor)
作用
  • 在对象销毁时自动调用,用于释放对象占用的资源(如内存、文件句柄)。
  • 名称是类名前~没有参数和返回类型
  • 不能重载(每个类只有一个析构函数)。
示例

析构函数:

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

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

使用场景:

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

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

六.this指针

1. 什么是 this 指针?

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

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

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

例如:

cpp 复制代码
class Person {
public:
    string name;
    void printName() {
        // 实际上等价于:cout << this->name << endl;
        cout << name << endl;
    }
};
3. 注意事项

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

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

七.拷贝构造函数

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

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

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

基本语法:

cpp 复制代码
ClassName(const ClassName& other);
二、浅拷贝(Shallow Copy)
什么是浅拷贝?
  • 默认的拷贝构造函数是浅拷贝
  • 直接复制成员变量的值(包括指针地址)
  • 如果类中有指针成员,会导致多个对象指向同一块内存
浅拷贝的问题
cpp 复制代码
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)
什么是深拷贝?
  • 需要手动实现拷贝构造函数
  • 为指针成员分配新的内存
  • 复制指针指向的内容,而不是复制指针地址
深拷贝解决方案
cpp 复制代码
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 指向不同内存
    // 析构时不会出现重复释放问题
}
四、关键总结
浅拷贝 深拷贝
复制内容 复制指针地址 复制指针指向的内容
内存安全性 多个对象共享同一内存,易导致崩溃 每个对象拥有独立内存,安全可靠
实现方式 默认拷贝构造函数 需要手动实现拷贝构造函数
适用场景 无动态内存分配的简单类 有指针成员或动态分配资源的类
相关推荐
夏日听雨眠1 分钟前
数据结构(单循环链表)
数据结构·链表
智算菩萨5 分钟前
【How Far Are We From AGI】4 AGI的“生理系统“——从算法架构到算力基座的工程革命
论文阅读·人工智能·深度学习·算法·ai·架构·agi
福赖7 分钟前
《算法:生产车间》
算法
-Excalibur-11 分钟前
IP数据包在计算机网络传输的全过程
java·网络·c++·笔记·python·网络协议·智能路由器
空空潍16 分钟前
LeetCode力扣 hot100一刷完结
算法·leetcode
leaves falling19 分钟前
搜索插入位置(第一个≥target的位置)
算法
历程里程碑19 分钟前
41 .UDP -3 群聊功能实现:线程池助力多客户端通信
linux·开发语言·网络·数据结构·c++·网络协议·udp
lcreek19 分钟前
LeetCode 1162.地图分析
算法·leetcode·bfs
山栀shanzhi21 分钟前
【FFmpeg】是什么是未压缩的裸流?
c++·ffmpeg
寒月小酒22 分钟前
3.20 OJ
算法