当前位置: 首页 > news >正文

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

一、默认成员函数

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.mrgr.cn/news/62380.html

相关文章:

  • 哈工大《理论力学》第九版课后答案解析及笔记PDF
  • git回滚间隔的提交
  • 网络安全包含哪些方面?如何加强网络安全建设?
  • SpringBoot 集成 Mybatis-Plus,LambdaQueryWrapper 使用方法
  • 基于springboot的社区团购系统设计与实现
  • 利用东芝TC78S600FTG提升效率:面向工程师的全面解
  • AI时代,哪种人更被需要?
  • 【传知代码】自动化细胞核分割与特征分析
  • flowable7.1.0功能
  • 单例 C++ 懒汉+恶汉
  • 前端面试题21 | 了解过媒体查询吗?它有哪些应用场景?
  • 《JVM第4课》程序计数器
  • 注册信息的提交
  • 不适合的学习方法
  • (5)数组
  • 【SAP FICO】八大业务_6货币资金管理
  • 数据采集-Kepware OPCUA 服务器实现
  • CNN在线识别手写中文
  • 返回数组中元素的数据类型numpy.dtype.name
  • 四季皆宜的网球场:气膜网球馆改造方案—轻空间
  • 刘艳兵-DBA016-在您的数据库中,SALES表存在于SH用户中,并且启用了统一审计。作为DBA,您成功执行了以下指令:
  • Spring Boot 配置文件详解与最佳实践
  • 第15天预编译
  • 组合两个表
  • 计算机组成原理之选择结构语句的机器级别表示
  • HTTPS对中间人真就束手就擒了?