C++面向对象5——C++关键字、构造函数与拷贝构造函数

this关键字

C++关键字this的深度解析

1. this指针的本质

在C++中,this是一个特殊的隐式指针,它存在于每个非静态成员函数内部,指向调用该函数的当前对象。其类型为:

  • 对于非const成员函数:ClassName* const(指针本身不可修改,但可修改指向的对象)
  • 对于const成员函数:const ClassName* const(指针和指向的对象均不可修改)

核心特性:

  • 无需显式声明,可直接在成员函数中使用
  • 解决成员变量与参数名冲突问题
  • 支持链式调用(返回*this
  • 在析构函数中可安全使用(对象尚未完全销毁)
2. this值与类对象取地址的比较
#include <iostream>
using namespace std;class Point {
public:int x, y;Point(int x, int y) : x(x), y(y) {}void printAddress() {cout << "对象地址: " << this << endl;  // 输出this指针的值}
};int main() {Point p(10, 20);cout << "直接取地址: " << &p << endl;  // 输出对象p的地址p.printAddress();                      // 输出成员函数中的this值// 验证两者相等if (&p == &p) {  // 等价于 if (&p == this) 在成员函数内部cout << "地址相等!" << endl;}return 0;
}

输出结果:

直接取地址: 0x7ffee2c5a1a0
对象地址: 0x7ffee2c5a1a0
地址相等!

关键结论:

  • this的值与直接对对象取地址(&object)完全相同
  • this是对象地址的别名,指向对象的起始内存位置
3. 使用this更新类成员
① 解决命名冲突
class Rectangle {
private:double length, width;public:Rectangle(double length, double width) {this->length = length;  // 左侧this->length指成员变量,右侧指参数this->width = width;}
};
② 在成员函数中修改当前对象
class Counter {
private:int count;public:void increment() {this->count++;  // 等价于 count++,但显式使用this}void reset() {this->count = 0;  // 重置当前对象的count}
};
4. 链式调用(返回*this

通过返回当前对象的引用(ClassName&),可以实现方法的连续调用。

class Calculator {
private:double value;public:Calculator(double initial = 0) : value(initial) {}// 加法:返回引用支持链式调用Calculator& add(double num) {this->value += num;return *this;  // 返回当前对象的引用}// 乘法:返回引用支持链式调用Calculator& multiply(double num) {this->value *= num;return *this;}double getResult() const {return this->value;}
};// 使用示例
Calculator calc(10);
double result = calc.add(5).multiply(2).getResult();  // 等价于 (10+5)*2 = 30

链式调用的优势:

  • 代码更简洁,减少临时变量
  • 提高可读性,表达连贯操作
  • 与STL算法和流操作符(如cout << a << b)风格一致
5. 在const成员函数中使用this

const成员函数中的this指针类型为const ClassName* const,不可修改对象状态。

class Vector {
private:double x, y;public:Vector(double x, double y) : x(x), y(y) {}// const成员函数:返回向量长度的平方double lengthSquared() const {// this的类型为const Vector* constreturn this->x * this->x + this->y * this->y;  // 只读访问}// 非const版本:允许修改向量Vector& scale(double factor) {this->x *= factor;this->y *= factor;return *this;}
};

注意事项:

  • const成员函数不能调用非const成员函数(可能修改对象)
  • 可通过const_cast强制转换thisconst属性,但可能导致未定义行为
6. 高级用法拓展
① 在析构函数中使用this
class Resource {
public:~Resource() {// 安全访问this(对象尚未完全销毁)log("Resource destroyed at address: " << this);}void log(const string& message) const {// 记录日志...}
};
② 在拷贝构造函数和赋值运算符中使用this
class MyClass {
public:// 拷贝构造函数MyClass(const MyClass& other) {// 通过this访问当前对象this->data = new int[other.size];// ... 复制数据 ...}// 赋值运算符重载MyClass& operator=(const MyClass& other) {if (this != &other) {  // 防止自赋值// 释放当前资源delete[] this->data;// ... 复制数据 ...}return *this;  // 返回当前对象引用}
};
③ 在模板类中使用this
template<typename T>
class Base {
protected:T value;public:void printValue() {cout << this->value << endl;  // 必须使用this访问模板基类成员}
};template<typename T>
class Derived : public Base<T> {
public:void setValue(const T& val) {this->value = val;  // 通过this明确指向基类成员}
};
7. 常见误区与注意事项
  1. 静态成员函数中无this指针

    class StaticClass {
    public:static void staticFunc() {// this;  // 错误:静态成员函数中没有this指针}
    };
    
  2. 空指针调用成员函数(危险)

    MyClass* ptr = nullptr;
    ptr->nonVirtualFunc();  // 如果函数不访问this,可能不崩溃(未定义行为)
    ptr->virtualFunc();     // 几乎肯定崩溃(访问虚函数表需要this)
    
  3. 避免返回局部对象的引用

    MyClass& badFunction() {MyClass local;return *this;  // 错误:返回局部对象的引用(悬空引用)
    }
    
总结:this的核心价值
  1. 明确对象身份:在成员函数中区分成员变量与局部变量
  2. 支持链式调用:通过返回*this实现流畅的API设计
  3. 安全访问对象:在拷贝构造、赋值运算符和析构函数中确保正确操作当前对象
  4. 模板编程需求:在模板类中显式指定基类成员

合理使用this指针可以提高代码的清晰度和安全性,是C++面向对象编程的重要工具。

构造函数与拷贝构造函数

C++构造函数与拷贝构造函数详解

一、构造函数(Constructor)介绍

构造函数是类的特殊成员函数,主要用于对象的初始化,具有以下特点:

  • 与类名相同,没有返回类型
  • 在对象创建时自动调用
  • 可以重载(存在多个不同参数的构造函数)
1. 构造函数的类型
(1)无参数构造函数
  • 没有参数的构造函数,用于创建对象时进行默认初始化
  • 示例
class Person {
public:string name;int age;// 无参数构造函数Person() {name = "Unknown";age = 0;}
};// 使用方式
Person p;  // 调用无参构造函数
(2)带部分参数的构造函数
  • 只初始化部分成员变量的构造函数
  • 示例
class Person {
public:string name;int age;// 带部分参数的构造函数Person(string n) {name = n;age = 18;  // 默认年龄}
};// 使用方式
Person p("Alice");  // 调用带部分参数的构造函数
(3)带全部参数的构造函数
  • 初始化所有成员变量的构造函数
  • 示例
class Person {
public:string name;int age;// 带全部参数的构造函数Person(string n, int a) {name = n;age = a;}
};// 使用方式
Person p("Bob", 25);  // 调用带全部参数的构造函数
(4)使用初始化列表的构造函数
  • 通过构造函数初始化列表(:)更高效地初始化成员变量
  • 优点:直接调用成员变量的构造函数,避免临时对象创建
  • 示例
class Person {
public:string name;int age;// 使用初始化列表的构造函数Person(string n, int a) : name(n), age(a) {// 构造函数体可空}
};
2. 构造函数的其他特性
  • 默认构造函数:编译器自动生成的无参构造函数(仅当类没有定义任何构造函数时)
  • 委托构造函数:一个构造函数调用另一个构造函数完成初始化
    class Person {
    public:string name;int age;Person() : Person("Unknown", 0) {}  // 委托给带参数的构造函数Person(string n, int a) : name(n), age(a) {}
    };
    
  • ** explicit 构造函数**:防止隐式类型转换的构造函数
    explicit Person(string n) : name(n), age(18) {}
    Person p = "Charlie";  // 错误,explicit禁止隐式转换
    Person p("Charlie");  // 正确
    
二、拷贝构造函数(Copy Constructor)详解
1. 基本概念与调用条件
(1)基本概念
  • 特殊的构造函数,用于从已存在的对象创建新对象
  • 格式:类名(const 类名& 变量名)
  • 示例
class Person {
public:string name;int age;Person(const Person& p) {  // 拷贝构造函数name = p.name;age = p.age;}
};
(2)调用条件(4种情况)
  1. 对象以值传递方式传入函数
    void func(Person p) { /*...*/ }  // 调用拷贝构造函数
    
  2. 函数以值传递方式返回对象
    Person func() { Person p;return p;  // 调用拷贝构造函数
    }
    
  3. 用一个对象初始化另一个同类型对象
    Person p1("Alice", 20);
    Person p2 = p1;  // 调用拷贝构造函数
    Person p3(p1);   // 等价于p3 = p1
    
  4. 初始化容器元素或数组时
    vector<Person> vec;
    vec.push_back(p1);  // 调用拷贝构造函数
    
2. 浅拷贝与深拷贝
(1)浅拷贝(默认拷贝行为)
  • 编译器自动生成的拷贝构造函数,仅复制指针地址而非内容
  • 问题:当对象包含动态分配内存时,多个对象指向同一内存,导致:
    • 释放内存冲突(double free)
    • 访问已释放内存(dangling pointer)
  • 示例(危险的浅拷贝)
class Data {
public:int* ptr;Data(int value) {ptr = new int(value);}// 未定义拷贝构造函数,使用默认浅拷贝
};int main() {Data d1(10);Data d2 = d1;  // 浅拷贝,d2.ptr = d1.ptrdelete d1.ptr;cout << *d2.ptr << endl;  // 访问已释放的内存,导致未定义行为return 0;
}
(2)深拷贝(自定义拷贝构造函数)
  • 手动复制指针指向的内容,而非指针本身
  • 解决方案
class Data {
public:int* ptr;Data(int value) {ptr = new int(value);}// 自定义深拷贝构造函数Data(const Data& d) {ptr = new int(*d.ptr);  // 分配新内存并复制值}~Data() {delete ptr;  // 析构函数释放内存}
};
3. C++的三个规则(The Rule of Three)
  • 当类需要自定义以下任一函数时,通常需要同时定义其他两个:
    1. 拷贝构造函数
    2. 拷贝赋值运算符(operator=)
    3. 析构函数(~类名)
  • 原因:三者逻辑紧密相关,需保持一致性
  • 示例
class Resource {
public:char* data;int size;Resource(int s) {size = s;data = new char[size];}// 三个规则示例~Resource() { delete[] data; }  // 析构函数Resource(const Resource& r) {  // 拷贝构造函数size = r.size;data = new char[size];memcpy(data, r.data, size);}Resource& operator=(const Resource& r) {  // 拷贝赋值运算符if (this != &r) {  // 避免自赋值delete[] data;size = r.size;data = new char[size];memcpy(data, r.data, size);}return *this;}
};
4. 避免不必要的拷贝
  • C++11 移动语义(Move Semantics):通过std::move转移资源所有权,避免深拷贝
    class BigObject {vector<int> data;
    public:// 移动构造函数BigObject(BigObject&& other) noexcept : data(std::move(other.data)) {}// 移动赋值运算符BigObject& operator=(BigObject&& other) noexcept {data = std::move(other.data);return *this;}
    };
    
  • 常量引用传参:用const &代替值传递,避免拷贝
    void processObject(const Object& obj) { /*...*/ }  // 避免拷贝
    
5. 拷贝构造函数的隐式调用
  • 即使不定义拷贝构造函数,编译器也会隐式生成一个默认版本
  • 例外情况:当类包含以下成员时,默认拷贝构造函数会被禁用:
    • 引用成员(int& ref
    • 常量成员(const int val
    • 没有默认构造函数的类成员
  • 示例(默认拷贝构造函数被禁用)
class MyClass {int& ref;       // 引用成员const int val;  // 常量成员public:MyClass(int& r, int v) : ref(r), val(v) {}// 无法生成默认拷贝构造函数,需手动定义
};
6. 禁用拷贝构造函数
  • C++11 方法:使用= delete显式禁用
    class NonCopyable {
    public:NonCopyable() = default;NonCopyable(const NonCopyable&) = delete;  // 禁用拷贝构造函数NonCopyable& operator=(const NonCopyable&) = delete;  // 禁用拷贝赋值
    };
    
  • C++98 方法:将拷贝构造函数设为私有且不实现
    class NonCopyable {
    private:NonCopyable(const NonCopyable&);  // 声明为私有NonCopyable& operator=(const NonCopyable&);
    public:NonCopyable() {}
    };
    
7. 拷贝构造函数总结
  • 核心作用:创建对象的副本,确保资源正确复制
  • 关键原则
    • 若类包含动态分配资源,必须自定义深拷贝构造函数
    • 遵循"三个规则"保持析构函数、拷贝构造、拷贝赋值的一致性
    • 利用C++11移动语义优化性能,减少不必要的拷贝
  • 性能影响:深拷贝可能带来较高开销,需根据场景权衡设计
三、扩展:构造函数与拷贝构造函数的其他特性
  • 构造函数初始化顺序:成员变量按声明顺序初始化,与初始化列表顺序无关
  • 拷贝构造函数与const:参数通常声明为const &,避免无法拷贝常量对象
  • 拷贝构造函数与异常:应保证拷贝过程中不抛出异常(使用noexcept修饰)
  • 聚合类的拷贝:聚合类(无构造函数、全公共成员)通过默认拷贝构造函数复制

通过合理设计构造函数和拷贝构造函数,可以确保对象初始化的安全性和高效性,避免内存错误和性能问题。

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

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

相关文章

人工智能专业:探索未来的智慧前沿

亲戚家的小孩刚高考完&#xff0c;问我人工智能专业是学什么、做什么的。趁机就写一篇吧&#xff01; 人工智能专业&#xff1a;探索未来的智慧前沿 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;无疑是当今最热门、最具颠覆性的技术之一。它正…

618风控战升级,瑞数信息“动态安全+AI”利剑出鞘

每年的618电商促销季&#xff0c;都是各大电商平台和商家的兵家必争之地。数以亿计的消费者涌入线上平台&#xff0c;期待已久的优惠券、秒杀商品如潮水般涌现&#xff0c;海量交易在瞬间达成&#xff0c;无疑是一场商业狂欢。 然而&#xff0c;在这场狂欢背后&#xff0c;自动…

神经网络的架构

神经网络中的基本术语 以上图为例&#xff0c;相关的术语描述如下&#xff1a; 最左边的称为输⼊层&#xff0c;其中的神经元称为输⼊神经元&#xff1b;最右边的&#xff0c;即输出层包含有输出神经元&#xff1b;本例中的输出神经元只有一个&#xff1b;中间层&#xff0c;既…

安全生产监测预警系统:构筑智能化的安全防线

安全生产监测预警系统是工业安全管理的核心工具&#xff0c;它利用物联网、大数据、人工智能等技术&#xff0c;实现对生产环境、设备运行和人员行为的全方位监测&#xff0c;确保风险早发现、早预警、早处置。其核心功能涵盖实时监测、智能预警、应急处置、数据分析与优化等多…

Java练习题精选6-10

Java练习题精选6-10 一、第六题二、第七题第八题第九题第十题 一、第六题 如何将两个变量的值进行交换&#xff1f;假设变量a1&#xff0c;b2。 public class Main {public static void main(String[] args) {int a 1;int b 2;int tmp;System.out.println("交换前a&qu…

【GESP】C++四级考试大纲知识点梳理, (2) 结构体和二维数组

GESP C四级官方考试大纲中&#xff0c;共有11条考点&#xff0c;本文针对第2条考点进行分析介绍。 &#xff08;2&#xff09;掌握 C结构体、二维及多维数组的基本概念及使用 四级其他考点回顾&#xff1a; 【GESP】C四级考试大纲知识点梳理, (1) 指针 全文详见&#xff1a;【G…

自动化测试--App自动化之项目实战脚本编写及封装流程

1.App测试范围 app自动化测试主要核心测试手机程序 测试方面&#xff1a; 功能测试 安装卸载测试 升级测试 兼容性测试 网络切换&#xff0c;中断测试 横竖屏切换 健壮性 2.测试环境的搭建 需要配置的环境 java jdk Java的环境 Android sdk 安卓环境 python环境…

【Unity】什么是前向渲染、延迟渲染、单通道渲染、多通道渲染?

好的&#xff0c;我们来深入剖析这些核心渲染概念&#xff0c;理解它们的原理、优缺点以及在Unity&#xff08;特别是URP&#xff09;中的应用。 核心概念&#xff1a;渲染路径 (Rendering Path) 渲染路径决定了光照和着色在场景中是如何计算和应用的。它定义了物体被绘制到屏…

OpenCV CUDA模块设备层-----GPU上执行线程安全的 “原子取最大值” 操作函数

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 这是一个 OpenCV 的 CUDA 模块&#xff08;cv::cudev&#xff09; 中封装的原子操作函数&#xff0c;用于在 GPU 上执行线程安全的 “原子取最大…

【nRF52832】【环境搭建 1】【ubuntu下搭建nRF52832开发环境】

本文讲述如何在 ubuntu 22.04 下开发 nRF52832. host 环境说明: $ uname -a Linux leo 6.8.0-60-generic #63~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 22 19:00:15 UTC 2 x86_64 x86_64 x86_64 GNU/Linux1. 安装软件 sudo apt install gcc-arm-none-eabisudo apt-get i…

【Nginx】403 Forbidden错误

当 Nginx 代理配置出现 403 Forbidden 错误时&#xff0c;通常是由于权限或配置问题导致。以下是常见原因和解决方案&#xff1a; 常见原因及解决方法 1. 后端服务器拒绝访问 原因&#xff1a;后端 HTTPS 服务配置了 IP 白名单或访问控制解决&#xff1a; 检查后端服务器&…

详解 `pip install -e .` 命令【PythonLinux】

详解 pip install -e . 命令 pip install -e . 是 Python 开发中一个非常有用的命令&#xff0c;用于以"可编辑"或"开发"模式安装当前目录中的 Python 包。 命令分解 pip: Python 的包管理工具install: pip 的子命令&#xff0c;用于安装包-e: 是 --edi…

将VSCode的配置迁移到Cursor

目录 方式一&#xff1a;一键导入配置 1.适用场景 2.导入前的配置 3.导入步骤 4.查看导入效果 5.原理 6.注意 方式二&#xff1a;手动迁移配置文件 1.使用场景 2.导入步骤 3.注意 方式一&#xff1a;一键导入配置 1.适用场景 VSCode和Cursor这两个编辑器&#xff0…

大规模流媒体系统架构实践

&#x1f4cb; 文章目录 系统概述 - 流媒体系统的基本挑战整体架构设计 - 分层架构与核心组件接入层设计 - CDN与负载均衡策略处理层架构 - 实时转码与分发存储层优化 - 多级缓存与热点数据监控与运维 - 全链路监控体系实战经验总结 - 踩坑指南与最佳实践 &#x1f3ac; 系统概…

Vue 3 + Vite 项目 px 转 vw 完整使用指南

Vue 3 Vite 项目 px 转 vw 完整使用指南 &#x1f680; 第一步&#xff1a;插件安装 1.1 安装命令 # 使用 npm 安装 npm install postcss-px-to-viewport-8-plugin --save-dev# 或使用 yarn 安装 yarn add postcss-px-to-viewport-8-plugin --dev# 或使用 pnpm 安装 pnpm a…

setsate()使用详解原理及注意事项

&#x1f4da; Flutter 状态管理系列文章目录 Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux) setState() 使用详解&#xff1a;原理及注意事项 InheritedWidget 组件使用及原理 Flutter 中 Provider 的使用、注…

ffmpeg环境配置

ffmpeg是一个跨平台功能强大的音视频处理工具。它不仅能够进行音视频的转换、剪切、合并等操作&#xff0c;还支持多种音视频格式的解码和编码。无论是处理高清视频还是音频文件&#xff0c;ffmpg都能提供高效且稳定的服务。其丰富的参数设置和插件支持&#xff0c;使得用户可以…

Lamp和友点CMS一键部署脚本(Rocky linux)

先上传youdiancms9.3.0.zip包&#xff0c;可以去官网下载 #!/bin/bash #function:install apache mysql php for youdiancms9.3.0ip$(hostname -I | awk {print $1}) yhyoudian passwordyoudian123#检查是否为root用户 if [ "$USER" ! "root" ]; thenecho…

Arm架构下麒麟V10桌面版安装MySQL

Arm架构下麒麟V10桌面版安装MySQL 文章目录 Arm架构下麒麟V10桌面版安装MySQL基础环境下载安装包安装步骤**一、准备工作****二、解压并配置 MySQL****三、初始化 MySQL****四、启动 MySQL 服务****五、设置环境变量****六、配置 MySQL****七、验证安装****常见问题****总结** …

Science Advances:皮肤附着触觉贴片,实现多功能和增强的触觉交互

可穿戴触觉界面可以通过向皮肤添加触觉刺激以及传递给用户的视觉和听觉信息来增强虚拟/增强现实系统中的沉浸式体验。研究人员介绍了一种平锥介电弹性体致动器&#xff08;FCDEA&#xff09;阵列&#xff0c;该阵列薄而柔软&#xff0c;能够响应大面积皮肤上的电压信号产生时空…