C++学习:类和对象(二)

news/2024/10/31 8:33:05/

一、默认成员函数

1. 什么是默认成员函数?

在C++中,每个类都有一些特殊的成员函数,如果程序员没有显式地声明,编译器会自动为类生成这些函数,这些函数称为默认成员函数

2. 默认成员函数列表

  • 默认构造函数(Default Constructor)
  • 默认析构函数(Destructor)
  • 默认拷贝构造函数(Copy Constructor)
  • 默认拷贝赋值运算符(Copy Assignment Operator)
  • 默认移动构造函数(Move Constructor,C++11引入)
  • 默认移动赋值运算符(Move Assignment Operator,C++11引入)

3. 编译器何时生成默认成员函数?

  • 显式声明:如果程序员没有提供某个默认成员函数的定义,编译器会根据需要自动生成
  • 特别注意:一旦程序员显式地声明了任何一个拷贝或移动操作,编译器将不会为该类生成移动操作,需要手动提供

4. 代码示例

#include <iostream>
using namespace std;class Example {
public:int value;// 未显式声明任何默认成员函数
};int main() {Example ex1;       // 调用默认构造函数ex1.value = 10;Example ex2 = ex1; // 调用默认拷贝构造函数Example ex3;ex3 = ex1;         // 调用默认拷贝赋值运算符cout << "ex1.value = " << ex1.value << endl;cout << "ex2.value = " << ex2.value << endl;cout << "ex3.value = " << ex3.value << endl;return 0;
}

二、构造函数

1. 什么是构造函数?

构造函数(Constructor)是在创建对象时自动调用的特殊成员函数,用于初始化对象的成员变量。构造函数的名称与类名相同

2. 特点

  • 没有返回类型(连void也没有)
  • 可以有参数(参数化构造函数)
  • 支持函数重载,即可以有多个构造函数
  • 可以在构造函数初始化列表中初始化成员变量

3. 默认构造函数

如果程序员未提供任何构造函数,编译器会为类生成一个默认构造函数,它对基本类型成员变量不进行初始化

4. 代码示例:

1.默认构造函数

#include <iostream>
using namespace std;class Person {
public:string name;int age;// 默认构造函数Person() {name = "Unknown";age = 0;}void display() const {cout << "姓名:" << name << ", 年龄:" << age << endl;}
};int main() {Person p; // 调用默认构造函数p.display();return 0;
}

2.参数化构造函数

#include <iostream>
using namespace std;class Person {
public:string name;int age;// 参数化构造函数Person(const string& n, int a) {name = n;age = a;}void display() const {cout << "姓名:" << name << ", 年龄:" << age << endl;}
};int main() {Person p("Alice", 25); // 调用参数化构造函数p.display();return 0;
}

 3.构造函数初始化列表

#include <iostream>
using namespace std;class Point {
private:int x;int y;public:// 使用初始化列表初始化成员变量Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {}void display() const {cout << "坐标:(" << x << ", " << y << ")" << endl;}
};int main() {Point pt(3, 4);pt.display();return 0;
}

5. 注意事项

  • 如果类中有const成员变量或引用类型成员,必须使用初始化列表进行初始化
  • 构造函数可以被重载,允许创建多个具有不同参数列表的构造函数

三、析构函数

1. 什么是析构函数?

析构函数(Destructor)是在对象生命周期结束时自动调用的特殊成员函数,用于释放对象占用的资源(如内存、文件等)。析构函数的名称是在类名前加上~符号

2. 特点

  • 没有参数
  • 没有返回类型
  • 每个类只有一个析构函数,不能重载
  • 编译器会自动调用析构函数,无需手动调用

3. 代码示例

#include <iostream>
using namespace std;class Resource {
public:Resource() {cout << "资源已分配。" << endl;}~Resource() {cout << "资源已释放。" << endl;}
};int main() {cout << "进入main函数。" << endl;{Resource res; // 创建对象,调用构造函数} // 离开作用域,调用析构函数cout << "退出main函数。" << endl;return 0;
}

4. 在析构函数中释放资源

当类中使用了动态内存分配(如使用new关键字),需要在析构函数中释放内存,防止内存泄漏

#include <iostream>
using namespace std;class Array {
private:int* data;int size;public:Array(int s) : size(s) {data = new int[size];cout << "数组已分配。" << endl;}~Array() {delete[] data;cout << "数组已释放。" << endl;}
};int main() {Array arr(10);// 使用数组...return 0;
}

5. 注意事项

  • 析构函数必须为公有成员函数,否则对象在离开作用域时无法正确销毁
  • 避免在析构函数中抛出异常,这可能导致程序不可预测的行为

四、拷贝构造函数

1. 什么是拷贝构造函数?

拷贝构造函数(Copy Constructor)是使用同类的另一个对象来初始化新对象时调用的构造函数。它用于定义对象的拷贝行为

2. 语法

ClassName(const ClassName& other);

参数为同类对象的引用,通常为const引用,避免不必要的拷贝

3. 默认拷贝构造函数

  • 如果程序员未提供拷贝构造函数,编译器会自动生成默认的拷贝构造函数,执行浅拷贝
  • 对于没有动态内存分配的类,默认的拷贝构造函数通常够用

4. 浅拷贝与深拷贝

  • 浅拷贝(Shallow Copy):拷贝对象的成员变量值,对于指针成员,仅拷贝指针值,两个对象指向同一内存位置
  • 深拷贝(Deep Copy):在拷贝指针成员时,为新对象分配独立的内存空间,并复制内容

5. 示例

1.默认拷贝构造函数(浅拷贝)

#include <iostream>
using namespace std;class Shallow {
public:int* data;Shallow(int val) {data = new int(val);}~Shallow() {delete data;}
};int main() {Shallow obj1(5);Shallow obj2 = obj1; // 调用默认拷贝构造函数cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 10;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}

问题:

由于是浅拷贝,obj1obj2data指向同一内存,当一个对象被析构时,内存被释放,另一个对象再使用时会导致悬空指针

2.自定义拷贝构造函数(深拷贝)

#include <iostream>
using namespace std;class Deep {
public:int* data;Deep(int val) {data = new int(val);}// 自定义拷贝构造函数Deep(const Deep& other) {data = new int(*(other.data));}~Deep() {delete data;}
};int main() {Deep obj1(5);Deep obj2 = obj1; // 调用自定义拷贝构造函数cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 10;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}

6. 注意事项

  • 拷贝构造函数的参数必须是引用,否则会导致无限递归
  • 当类中有指针成员,且需要独立的内存空间,必须提供自定义的拷贝构造函数(深拷贝)

五、赋值运算符函数

1. 什么是赋值运算符函数?

赋值运算符函数(Assignment Operator Function)用于定义对象间赋值行为(operator=)。类似于拷贝构造函数,它也需要考虑浅拷贝和深拷贝

2. 语法

ClassName& operator=(const ClassName& other);
  • 返回类型为引用,返回当前对象自身*this,以支持链式赋值
  • 参数为同类对象的**const引用**

3. 默认赋值运算符

  • 如果程序员未提供赋值运算符函数,编译器会生成默认的赋值运算符,执行浅拷贝

4. 示例

1.默认赋值运算符(浅拷贝)

#include <iostream>
using namespace std;class Shallow {
public:int* data;Shallow(int val) {data = new int(val);}~Shallow() {delete data;}
};int main() {Shallow obj1(5);Shallow obj2(10);obj2 = obj1; // 使用默认赋值运算符cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 15;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}

2.自定义赋值运算符函数(深拷贝)

#include <iostream>
using namespace std;class Deep {
public:int* data;Deep(int val) {data = new int(val);}Deep(const Deep& other) {data = new int(*(other.data));}// 自定义赋值运算符函数Deep& operator=(const Deep& other) {if (this == &other) {return *this; // 检查自赋值}delete data; // 释放原有内存data = new int(*(other.data)); // 分配新内存并拷贝return *this;}~Deep() {delete data;}
};int main() {Deep obj1(5);Deep obj2(10);obj2 = obj1; // 使用自定义赋值运算符cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 15;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}

5. 注意事项

  • 检查自赋值:在赋值运算符函数中,应检查thisother是否为同一对象,避免释放自己
  • 释放原有资源:在进行赋值前,应释放对象原有的资源,防止内存泄漏
  • 返回*this的引用,支持链式赋值

六、const成员函数

1. 什么是const成员函数?

const成员函数是指在函数声明后加上const关键字的成员函数,表示该函数不会修改对象的成员变量(除非成员变量被声明为mutable

2. 语法

返回类型 函数名(参数列表) const;

3. 特点

  • const成员函数只能调用其他const成员函数,不能调用非const成员函数
  • 可以被const对象调用,而非const成员函数不能被const对象调用

4. 代码示例

#include <iostream>
using namespace std;class Sample {
private:int value;public:Sample(int v) : value(v) {}int getValue() const {return value;}void setValue(int v) {value = v;}
};int main() {const Sample s(10); // 常量对象cout << "值是:" << s.getValue() << endl;// s.setValue(20); // 错误,不能调用非const成员函数return 0;
}

5. const对象和成员函数

  • **const对象:**对象被声明为const,只能调用其const成员函数,不能修改成员变量
  • **非const对象:**可以调用所有成员函数,包括const和非const

6. 成员函数重载

可以根据const性对成员函数进行重载
代码示例:

#include <iostream>
using namespace std;class Example {
public:void func() {cout << "非const版本的func()" << endl;}void func() const {cout << "const版本的func()" << endl;}
};int main() {Example e;e.func(); // 调用非const版本const Example ce;ce.func(); // 调用const版本return 0;
}

七、取地址及const取地址操作符重载

1. 取地址操作符operator&

默认情况下,对象的取地址操作会返回对象的内存地址。可以通过重载operator&来改变取地址操作的行为

2. 为什么需要重载取地址操作符?

  • 在某些情况下,我们希望隐藏对象的内部地址,或提供特殊的地址计算方式
  • 可以用于智能指针的实现

3. 语法

ClassName* operator&();
const ClassName* operator&() const;

4. 示例

1.重载取地址操作符

#include <iostream>
using namespace std;class MyClass {
public:int value;MyClass(int v) : value(v) {}// 重载取地址操作符MyClass* operator&() {cout << "自定义取地址操作符被调用。" << endl;return this;}// 重载const版本const MyClass* operator&() const {cout << "自定义const取地址操作符被调用。" << endl;return this;}
};int main() {MyClass obj(10);MyClass* ptr = &obj; // 调用自定义取地址操作符cout << "value = " << ptr->value << endl;const MyClass cobj(20);const MyClass* cptr = &cobj; // 调用自定义const取地址操作符cout << "const value = " << cptr->value << endl;return 0;
}

5. 注意事项

  • 小心使用:重载取地址操作符可能导致代码难以理解和维护,应该谨慎使用
  • 避免陷阱:重载后,可能会影响模板代码或标准库的使用,需要确保兼容性

http://www.ppmy.cn/news/1543309.html

相关文章

【QT】Qt窗口(下)

个人主页~ Qt窗口&#xff08;下&#xff09;~ Qt窗口 五、对话框2、Qt内置对话框&#xff08;1&#xff09;Message Box&#xff08;2&#xff09;QColorDialog&#xff08;3&#xff09;QFileDialog&#xff08;4&#xff09;QFontDialog&#xff08;5&#xff09;QInputDia…

【JavaEE】【多线程】进阶知识

目录 一、常见的锁策略1.1 悲观锁 vs 乐观锁1.2 重量级锁 vs 轻量级锁1.3 挂起等待锁 vs 自旋锁1.4 普通互斥锁 vs 读写锁1.5 可重入锁 vs 不可重入锁1.6 不公平锁 vs 公平锁 二、synchronized特性2.1 synchronized的锁策略2.2 synchronized加锁过程2.3 其它优化措施 三、CAS3.…

Kubeadm搭建k8s

一、架构 节点名称规格IP地址安装组件master012C/4G&#xff0c;cpu核心数要求大于2192.168.88.76docker、kubeadm、kubelet、kubectl、flannelnode012C/2G192.168.88.20docker、kubeadm、kubelet、kubectl、flannelnode022C/2G192.168.88.21docker、kubeadm、kubelet、kubect…

大数据技术的前景如何?

在当今数字化迅猛发展的时代&#xff0c;大数据技术的前景显得尤为广阔。随着数据量的激增&#xff0c;如何有效利用这些数据成为了各行各业关注的焦点。未来五年&#xff0c;大数据技术的发展趋势可以从市场规模、技术融合、行业应用和政策支持等多个方面进行深入分析。 1. 市…

excel指定单元格输入相同的值,比如给D1~D10000输入现在的值

注意&#xff0c;一点不用用WPS&#xff0c;不然运行宏是会报&#xff1a;Droiact-Module1,第1行等Λ列语法错误: Unexpected identifier 步骤 1&#xff0c;altF11打开宏 2&#xff0c;输入脚本 3&#xff0c;点击运行按钮 成功后会看看到

记录生活之JSON.parse坑 -- 【JavaScript】

目录 起因 问题 解决 因为是太简单的问题了&#xff0c;无法百度。&#xff08;他们只会告诉你key取值取错了或者的确是没有对应的value值&#xff09; js中的对象是只能用string || symbol类型来定义&#xff0c;所以obj[item.label]没有问题 那么问题出现哪里呢&#x…

logback日志脱敏后异步写入文件

大家项目中肯定都会用到日志打印&#xff0c;目的是为了以后线上排查问题方便&#xff0c;但是有些企业对输出的日志包含的敏感(比如&#xff1a;用户身份证号&#xff0c;银行卡号&#xff0c;手机号等)信息要进行脱敏处理。 哎&#xff01;我们最近就遇到了日志脱敏的改造。可…

推荐一款优秀的pdf编辑器:Ashampoo PDF Pro

Ashampoo PDF Pro是管理和编辑 PDF 文档的完整解决方案。程序拥有您创建、转换、编辑和保护文档所需的一切功能。根据需要可以创建特定大小的文档&#xff0c;跨设备可读&#xff0c;还可以保护文件。现在您还能像编辑Word文档一样编辑PDF! 软件特点 轻松处理文字 如 Microso…