c++面向对象的三大特性:
封装、继承、多态
对象 = 属性 + 行为
封装
封装的意义
- class 代表一个类,后面接类的名称
-class Circle {...};
定义一个圆类 (结尾有分号;)- { }里包括
访问权限、属性、行为
- 属性和行为统称为:成员
- 属性:成员属性、成员变量
- 行为:成员函数、成员方法
class Circle
{
//访问权限
//公共权限
public:
//属性(变量)
int r;
//行为(一般是一个函数)
//获取圆的周长
double zc()
{
return 2*PI*r;
}
}; // 类的结尾有分号 “ ;” ,和struct一样!
main()
{
//通过圆类创建具体的圆(对象)
//实例化 (通过一个类 创建一个对象的过程)
Circle c1;
//给圆对象的属性进行赋值
c1.r = 10;
cout << "圆的周长为: " << c1.zc() << endl ; //注意是c1.zc(),不是c1.zc
}
练习:类中包括学生的姓名和id,且能够实现他们的输入和输出
#include<iostream>
#include<string>
using namespace std;
class Student {
public:
string name; // 姓名
int id; // 学号
// 输入学生信息
void Input() {
getline(cin, name);
cin >> id;
}
// 输出学生信息
void Output() {
cout << "学生姓名:" << name << endl;
cout << "学生学号:" << id << endl;
}
};
int main() {
Student stu;
stu.Input(); // 输入学生信息
stu.Output(); // 输出学生信息
return 0;
}
访问权限
- 类在设计时,可以把属性和行为放在不同的权限下,加以控制
三种:
保护权限:儿子可以访问父亲中的保护内容(车)
私有权限:儿子不可以访问父亲的私有内容(密码)
class MyClass {
public:
int public_data;
private:
int private_data;
protected:
int protected_data;
public:
void setPrivateData(int value) {
private_data = value;
}
int getPrivateData() {
return private_data;
}
};
int main() {
MyClass obj;
obj.public_data = 42; // 可以直接访问 MyClass 中的 public 数据成员
// obj.private_data = 42; // 错误:无法直接访问 MyClass 中的 private 数据成员
// obj.protected_data = 42; // 错误:无法直接访问 MyClass 中的 protected 数据成员
obj.setPrivateData(42); // 可以通过公有成员函数访问 MyClass 中的 private 数据成员
int x = obj.getPrivateData();
return 0;
}
struct和class的区别
- 区别:
默认的访问权限不同
- struct:默认权限为公共
- class:默认权限为私有
成员属性私有化
- 优点
1. 将所有成员属性设为私有,可以自己控制读写权限
2. 对于写权限,我们可以检测数据的有效性
class Person {
public:
// 设置姓名函数(公共)
void SetName(string name) {
m_name = name;
}
// 获取姓名函数(公共)
string GetName() {
return m_name;
}
private:
// 姓名属性(私)
string m_name;
};
int main() {
Person p;
p.SetName("Tom"); //可以设置名字
cout << p.GetName() << endl; // 输出 Tom --->可以获取名字
//cout << p.m_name << endl; // 编译错误,私有成员变量无法在类的外部访问
}
对象的初始化和清理
构造函数与析构函数
构造函数:
- 用于创建对象时进行初始化。与其他成员函数不同的是,构造函数的名称必须与类名相同,而且没有返回类型(包括 void),因为构造函数的返回值是类的实例本身。
- 语法:
类名 (...) {...}
—>结尾不用分号;
- 函数名称和类名相同
- 没有返回值,也不用写void
- 函数可以有参数,因此可以发生重载
- 程序在创建对象时会自动调用构造函数,无需手动调用,且只会调用一次
- 有什么作用?
- 用于在创建对象时初始化对象的属性和状态。它在对象创建时被自动调用,通常用于执行一些必要的初始化操作,例如给对象的属性赋初值
- 因为是要进行赋值,所以一般是在public公共作用域下
- 构造函数和class里面的其他函数有什么区别?
- 在一个类中,构造函数是一种特殊的函数,用于在创建对象时初始化对象的属性和状态。与其他函数不同,构造函数在创建对象时自动调用,并且只被调用一次。构造函数的名称通常与类名称相同。
- 其他函数(也称为普通成员函数)是在对象创建后,可以被对象调用的函数。它们通常用于执行一些特定的操作,例如修改对象的属性或者执行某些计算。与构造函数不同,其他函数可以被多次调用,并且可以根据需要具有不同的参数和返回值。
-
- 总的来说,构造函数和其他函数的主要区别在于它们的用途和调用方式
。构造函数用于初始化对象,而其他函数用于执行特定的操作。构造函数只被调用一次,而其他函数可以被多次调用。此外,构造函数的名称通常与类名称相同,而其他函数的名称可以根据需要自定义。
- 其他函数的具体作用?
析构函数
- 语法:
~ 类名() {...}
—>前面有~号 ,( )里没有参数
- 函数名称和类名相同 ,在名称前加上~
- 没有返回值,也不用写void
- 函数不可以有参数,因此
不可以
发生重载- 程序在对象销毁时会自动调用析构函数,无需手动调用,且只会调用一次
- 有什么作用?
- 它在对象被销毁时自动调用,用于执行对象的清理操作。通常,析构函数用于
释放对象所占用的资源
,例如动态分配的内存、打开的文件或网络连接等。- 如果不使用析构函数,可能会导致内存泄漏或其他资源泄漏
代码
#include<iostream>
using namespace std;
class Person {
private:
string name;
int age;
public:
// 构造函数
Person(string n, int a) {
name = n;
age = a;
cout << "构造函数被调用" << endl;
}
// 析构函数
~Person() {
cout << "析构函数被调用" << endl;
}
// 成员函数
void sayHello() {
cout << "Hello, my name is " << name << " and I'm " << age << " years old." << endl;
}
};
int main() {
// 创建一个 Person 对象:person1
Person person1("Alice", 25); //这里相当是调用了构造函数 --->体现了构造函数的作用
// 调用成员函数
person1.sayHello();
// 对象会在这里自动销毁,因此会调用析构函数
return 0;
}
- 注:
Person person1("Alice", 25);
- 这里相当是调用了构造函数,Alice、25作为两个参数传入构造函数中,
通过构造函数
对class private: 中的 name 和 age 进行赋值- 而不是进行直接的赋值
- 因为:
在类的 private 访问修饰符中声明的变量无法在类的外部直接赋值。为了修改类中的私有变量,你需要提供公共的成员函数(通常是 setter 函数)来修改这些变量。
拷贝构造
- 拷贝构造函数
是一种特殊的构造函数
,它接受一个同类型的对象作为参数,并用该对象的属性和状态来初始化新对象。下面是一些拷贝构造函数的使用场景: - 注:前提是在定义的类中有拷贝函数的定义 (其实如果没写拷贝构造的话,编译器会自动生成一个拷贝构造函数,内容为值拷贝,即将a中的所有东西拷贝到b中)
对象作为函数参数传递时
:当一个对象作为函数的参数传递时,如果没有使用引用或指针,而是直接传递对象本身,那么会触发拷贝构造函数。例如:
void func(MyClass obj) {
// ...
}
int main() {
MyClass obj1;
func(obj1); // 触发拷贝构造函数 --->只是值传递而已,函数里对obj的修改不会影响外边obj1的值
return 0;
}
对象作为函数返回值返回时
:当一个对象作为函数的返回值返回时,如果没有使用引用或指针,而是直接返回对象本身,那么会触发拷贝构造函数。例如:
- 一般要返回对象,会使用引用Myclass& func(),若返回的对象是调用者自身的话,则return *this
MyClass func() {
MyClass obj;
// ...
return obj; // 触发拷贝构造函数 --->拷贝了一份作为返回值返回
}
int main() {
MyClass obj1;
MyClass obj2 = func(); // 触发拷贝构造函数
return 0;
}
对象初始化时使用另一个对象的值进行初始化
:当一个对象使用另一个对象的值进行初始化时,例如通过另一个对象进行赋值或初始化,那么会触发拷贝构造函数。例如:
MyClass obj1;
MyClass obj2 = obj1; // 触发拷贝构造函数
MyClass obj3(obj1); // 触发拷贝构造函数 --->这种最方便(括号法)
在以上场景中,拷贝构造函数的作用是将一个已有对象的属性和状态复制到一个新的对象中,以确保新对象的属性和状态与原始对象相同。需要注意的是,如果对象中存在动态分配的内存或其他资源,拷贝构造函数需要正确地处理这些资源,以避免内存泄漏或其他问题。
代码(泰裤版 )(包括:默认构造、带参数构造、拷贝构造)
#include <iostream>
using namespace std;
class MyClass {
private:
int value;
public:
// 默认构造函数
MyClass() {
value = 0;
cout << "Default constructor called." << endl;
}
// 带参数的构造函数
MyClass(int v) {
value = v;
cout << "Parameterized constructor called." << endl;
}
// 拷贝构造函数
MyClass(const MyClass& other) {
//注意我们不需要修改原来的值,所以传的值用const修饰,且是“引用&”
value = other.value;
cout << "Copy constructor called." << endl;
}
// 成员函数
void printValue() {
cout << "Value = " << value << endl;
}
};
int main() {
// 使用默认构造函数创建对象
MyClass obj1; //不要这样:obj1 ( ) ---> 没有括号()
obj1.printValue();
// 使用带参数的构造函数创建对象
MyClass obj2(10);
obj2.printValue();
// 使用拷贝构造函数创建对象
MyClass obj3 = obj2; //用括号法也ok
obj3.printValue();
return 0;
}
- 在上面的示例中,我们定义了一个MyClass类,并在其中实现了默认构造函数、带参数的构造函数和拷贝构造函数。我们还实现了一个printValue成员函数,用于打印对象的value属性。
- 在main函数中,我们首先使用默认构造函数创建了一个对象obj1,然后使用带参数的构造函数创建了一个对象obj2,并将其value属性设置为10。接着,我们使用拷贝构造函数创建了一个对象obj3,并将其初始化为obj2。最后,我们分别打印了三个对象的value属性。
- 输出结果:
太裤🌶
注:
构造函数的调用原则
用户提供有参构造函数,c++不提供默认无参构造,但提供默认拷贝构造
用户提供拷贝构造函数,c++不再提供其他任何构造函数
深拷贝与浅拷贝
- 浅拷贝:简单的赋值拷贝,和原来是同一块内存
- 深拷贝:在堆区重新申请空间,进行拷贝操作
(如果拷贝的数据成员没有指针类型,那应该没什么问题吧)
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
—> 视频说明
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
string name;
int age;
// 深拷贝
Person* DeepCopy() {
Person* p = new Person();
p->name = this->name;
p->age = this->age;
return p;
}
// 浅拷贝
Person* ShallowCopy() {
return this;
}
};
int main() {
// 创建一个原始对象
Person* john = new Person();
john->name = "John";
john->age = 25;
// 浅拷贝
Person* johnCopy = john->ShallowCopy();
// 修改原始对象的属性
john->name = "Jack";
john->age = 30;
// 输出浅拷贝对象的属性
cout << johnCopy->name << endl; // 输出 Jack
cout << johnCopy->age << endl; // 输出 30
// 深拷贝
johnCopy = john->DeepCopy();
// 修改原始对象的属性
john->name = "Tom";
john->age = 35;
// 输出深拷贝对象的属性
cout << johnCopy->name << endl; // 输出 Jack
cout << johnCopy->age << endl; // 输出 30
delete johnCopy;
delete john;
return 0;
}
- 所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。
- 在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间
- 如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝
- 如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行 “浅拷贝” ,即将被拷贝对象的 数据成员的值一一赋值给新创建的对象(简单的赋值而已) ,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针时则会导致两次重复delete而出错
链接
初始化列表赋初值
#include<iostream>
using namespace std;
class goods
{
private:
int pen;
int pencil;
public:
// 带参数的构造函数(用于变量的初始化)-->函数名要和类的名字一样,返回值
//传统方式初始化
// goods(int a, int b)
// {
// pen = a;
// pencil = b;
// }
//初始化列表方式初始化
goods(int a, int b):pen(a),pencil(b)
{
}
//成员函数 --->函数名不一定要和类的名字一样,且有返回值
void print_goods()
{
cout << "pen的价格是:" << pen << endl;
cout << "pencil的价格是:"<< pencil << endl;
}
};
int main()
{
goods mygoods(12,20); //两种不同的初始化均用该行代码
mygoods.print_goods();
}
对指针指向的类对象赋初值
- 动态内存分配的赋初值
- 非动态内存分配版
类对象作为类成员
- c++类中的成员可以是另一个类的对象,称该成员为对象成员
class A {
}
class B
{
A a;
}
B类中有对象A作为成员,A为对象成员
#include<iostream>
#include<string>
using namespace std;
class phone
{
// private: //不能设置为私有,否者person类里的m_phone.m_phonename该语句报错,显示无法访问m_phonename
public:
string m_phonename;
public:
phone (string a):m_phonename(a)
{
cout << "phone 构造函数调用" << endl;
}
~phone ()
{
cout << "phone 析构函数调用" << endl;
}
};
class person
{
private:
string m_name; //普通的数据成员(person类的属性/变量)
phone m_phone; //phone类对象成员
public:
person (string a, string b):m_name(a),m_phone(b)
{
cout << "person 构造函数调用" << endl;
}
~person()
{
cout << "person 析构函数调用" << endl;
}
void print_()
{
cout << m_name << "的手机是:" << m_phone.m_phonename <<endl;
}
};
int main()
{
person p("张三","HUAWEI");
p.print_();
}
运行结果:
- 原因:
- 当类中成员是其他类对象时,构造的顺序是:先调用对象成员的构造,再调用本类构造
- 析构顺序与构造相反
静态成员变量/函数(static)
- 静态成员变量:
- 该变量不属于某个对象,所有对象共享同一份数据(一个人改变其值,所有人看到的是改变过的值,
而非只是属于某个对象里的
)- 所以静态成员变量有两种访问方式:
- 通过
对象
进行访问
person p;
cout << p.m_A << endl; - 通过
类名
进行访问 —>非静态成员变量不行
cout << person :: m_A <<endl;
- 通过
- 所以静态成员变量有两种访问方式:
- 在编译阶段分配内存(在生成exe程序之前)
- 类内声明,类外初始化(否则无法访问该变量的内存)
- 也可以设置访问权限(当为私有时,也能在类外初始化,只是访问不了了)
- 该变量不属于某个对象,所有对象共享同一份数据(一个人改变其值,所有人看到的是改变过的值,
静态成员变量初始化
- 注:类外的初始化不用加static、但 要声明在哪个空间下的
class MyClass {
public:
static int myStaticVar;
};
int MyClass::myStaticVar = 0; // 在类外进行初始化
int main() {
MyClass p1;
cout << P1.myStaticVar << endl; // 0
MyClass p2;
p2.myStaticVar = 10;
cout << P1.myStaticVar << endl; // 10 ,即使是在p2里修改myStaticVar的值,但p1里的也会随之改变
}
- 静态成员函数
- 所有对象共享同一个函数
- 所以静态成员函数有两种访问方式:
- 通过
对象
进行访问
person p;
cout << p.fun( ) << endl; - 通过
类名
进行访问 —>非静态成员变量不行
cout << person :: fun( ) <<endl;
- 通过
- 所以静态成员函数有两种访问方式:
- 静态成员函数只能访问静态变量
- 非静态成员变量不行,因为无法区分该变量是哪个对象的
- 静态成员变量不属于哪个具体对象的,所以可以~
- 也可以设置访问权限
- 所有对象共享同一个函数
class MyClass {
private:
static int count;
public:
static void incrementCount() {
count++;
}
static int getCount() {
return count;
}
};
int MyClass::count = 0;
int main() {
MyClass::incrementCount(); // +1
MyClass::incrementCount(); // +1
int count = MyClass::getCount(); // 2
}
c++对象模型和this指针
成员变量和成员函数分开存储
空对象
占用内存空间为1字节
- c++编译器会给每个空对象分配一个字节空间,为了区分空对象占内存的位置
- 每个空对象也应该有一个独一无二的内存地址
class person
{
};
int main()
{
person p;
cout << sizeof(p) << endl; // 1
}
成员变量 和 成员函数 分开存储
- 只有非静态成员变量属于类的对象上
class person
{
int A; // 非静态成员变量 ---> 属于类的对象上
static int B; //静态成员变量 ---> 不属于任何类的对象上
// 有没有这句,结果都为4
void fun(){
}; // 非静态成员函数 ---> 不属于任何类的对象上
//有没有这句,结果都为4
static void fun(){
}; // 静态成员函数 ---> 不属于任何类的对象上
//有没有这句,结果都为4
};
int person::B = 0;
int main()
{
person p;
cout << sizeof(p) << endl; // 4
}
this指针
- this指针指向被调用的成员函数所属的对象
- 对象p1调用了成员函数, this就指向p1
–
- 对象p1调用了成员函数, this就指向p1
- 用途:
- 当成员变量和成员函数的参数同名时,用来区分他们
class Point {
public:
void setX(int x) {
this->x = x; // 明确指明要操作的是成员变量 x
}
private:
int x;
};
- 成员函数返回当前对象的引用(实现链式调用)
class Point
{
public:
Point &move(int dx, int dy)
{
x += dx;
y += dy;
cout << x << " " << y << endl;
return *this;
}
private:
int x = 0;
int y = 0;
};
int main()
{
Point p;
p.move(1, 2).move(3, 4).move(5, 6); // 链式调用
}
结果:
- 首先调用 p.move(1, 2),然后将返回的对象再次调用 move(3, 4),最后再次调用 move(5, 6)
- 为了能够链式调用多个函数,返回的对象引用必须指向同一个对象。因此,在实现返回对象引用的成员函数时,应该返回 *this,而非一个新的对象
- 所以函数的返回值应是引用类型Point &,而非Point —> 如果是Point ,则发生拷贝构造,产生另一个新的对象
- plus(list任务时遇到的迭代器、运算符重载有关的)
// 拷贝赋值
list<T> &operator=(const list<T> <)
{
if (this != <) // 要 < 吗??? --->必须要!!! lt是一个普通的链表,而this是一个指针,所以要用取地址符&取出lt的地址,再和指针this里面存的地址作比较,如果相同,则说明调用该函数的A和原来的B是一样的,若不同,就要执行拷贝赋值操作
//因为是运算符重载,所以在用到 链表A=链表B 时(即需要将链表B赋值给链表A时), A是调用这个重载函数的发起方,所以该函数里的this指针指向的就是链表A,而链表B则作为函数的参数,通过引用&变成链表lt,在函数中以lt的形式存在,并通过一系列操作,将lt的每个节点的值都赋给this所指的那个链表(也就是链表A)
{
clear(); // 既然不相等,就先清空this所指向的链表,也就是链表A。但为什么clear函数里没有出现this? --->只是一般默认head_前面的this不写, 其实this->head_ 和 head_ 是等价的
for (auto it = lt.begin(); it != lt.end(); ++it)
{
push_back(*it);
}
}
return *this;
}
空指针访问成员函数
-
person *p = NULL;
-
可以访问部分类中的成员,但不建议(如,访问成员变量m_A时就wrong,因为在类中m_A是以this->m_A存在的,而p->NULL,因此this没有指向的实体对象,也指向NULL,所以无法访问m_A了)
-
一般如下操作:
person p; //定义一个类对象p
person *ptr = &p; //定义一个指向person类对象的指针,指向p对象
person *ptr = new ptr; //动态内存分配
...
delete ptr;
- 要赋初值的话,见:初始化列表赋初值-对指针指向的类对象赋初值
const修饰成员函数
常函数
- 成员函数后加const
性质:
- 常函数内不可以修改成员属性m_A
- 成员属性声明时 加关键字mutable后,在常函数中依然可以修改
class person
{
public:
void func() const // 常函数
{
m_A = 0;// 不行
m_B = 0; //可以
}
m_A = 10;
mutable m_B = 20;
};
–
常对象
- 声明对象前加const
性质:
- 常对象只能调用常函数
class person
{
public: //必须要加,因为默认为privat
void func1() const // 常函数
{
...
}
void func2() // 普通函数
{
...
}
};
int main()
{
const person p; // 常对象
p.func1(); // 对
p.func2(); // 错
}
友元
- 让一个函数、类访问另一个类中的私有成员
- 三种实现:
- 全局函数做友元
- 类做友元
- 成员函数做友元
全局函数作友元
- 在全局函数(即不在类中的函数)的最前面加上friend
class Building
{
//相当于一个函数声明,表示gay这个函数是类Building的好朋友,可以谁便访问任何成员,包括private的
friend void gay(Building * building); // 前面加上`friend`,后面加上`;`
public:
Building()
{
this->m_sittingroom = "客厅";
this->m_bedroom = "卧室";
}
string m_sittingroom;
private:
string m_bedroom;
};
// 全局函数
void gay(Building * building)
{
cout << "gay正在访问:" << building->m_sittingroom << endl;
cout << "gay正在访问:" << building->m_bedroom << endl; // 如果没有加上友元的声明,那这行会报错(无法访问私有变量m_bedroom)
}
int main()
{
Building p;
//法一:
Building *ptr = &p;
gay(ptr);
//法二:
gay(&p);
}
类作友元
- 在类的最前面加上friend
class Building
{
friend class Gay; // 这样Gay里的成员函数 可以访问Building里的任何变量(包括private)
...
};
class Gay
{
...
};
成员函数作友元
- 在成员函数最前面加上friend
class Building
{
friend void Gay::visit_1(); // Gay里的成员函数visit_1 可以访问Building的任何东西,但Gay中的visit_2因为没有声明为友元,因此不能访问
//即,只对有申明为友元的函数才有效
};
class Gay
{
void visit_1()
{
...
}
void visit_2()
{
...
}
};
operator重载
- 对已有的运算符重新进行定义,赋予另一种功能,以适应不同的数据类型
- ++ i 不仅是自增,还能进行链表中节点的后移++p(等价于p = p->next)
- 当然,实现的前提是要进行运算符的重载operator,自己写一遍,告诉编译器++还能干嘛
– - (好像都是在类里定义,由类对象进行调用)
–
加号运算符重载
- 注:成员函数重载(顾名思义是定义在类里的) —> 在简化前为p1.operator+(p2);
- 这里表示的是由p1调用operator这个函数,p2作为参数传入该成员函数中
- 所以在成员函数中,this指的是p1,将p1的值和传进来的p2的值进行相加,相加结果赋值给新创的对象temp,然后作为该函数的返回值进行返回
- 函数外的p3接收函数的返回值,被赋予temp 的值,也就是p1+p2的值
左移运算符重载
- 注:只能通过全局函数进行实现
- 简化前是operator<<(cout, p1)
- 简化后就直接cout << p1
- 因为函数的返回值还是cout,因此可以以多个<< 一起连用,实现链式编程
递增运算符重载
- 注:两个++的函数返回类型不同,一个是返回引用,另一个是返回对象(因为temp是局部变量,有生命周期,不可做引用返回)
- 后置++的参数是int,作用是占位符,没有实际作用,只是为了区分两种++
小组任务中的+±-
// 迭代到下一个节点
//++it
iterator_ &operator++()
{
node_ptr_ = node_ptr_->next_;
return *this;
}
// 迭代到前一个节点
//--it
iterator_ &operator--()
{
node_ptr_ = node_ptr_->prev_;
return *this;
}
// it++
iterator operator++(int) // int参数作为占位符来区分前置++和后置++,该参数没有实际用途
{
iterator tmp(this->node_ptr_);
node_ptr_ = node_ptr_->next_;
return tmp;
}
// it--
iterator operator--(int)
{
iterator tmp(node_ptr_);
node_ptr_ = node_ptr_->prev_;
return tmp;
}
赋值运算符重载
-
其实class类里已经有=的赋值运算符,比如两个对象p1、p2
-
p2=p1就是将p1里的东西拷贝一份给p2
-
但默认的是浅拷贝,即如果class类中有动态分配到堆区的成员变量int* a = new int的话,p1在执行析构函数中delete a时,会把a指向的那块内存给释放掉,当p2执行析构函数准备delete a时,因为a已经被释放掉了,编译器就会报错
-
这就需要我们自己实现一下赋值=,让他变成深拷贝
定义了一个名为 operator= 的成员函数,它实现了自定义的赋值操作。在函数体内,我们首先判断被赋值的对象是否是当前对象本身,如果是,则直接返回当前对象的引用;否则,释放原有的内存空间,重新分配内存,并将被赋值对象的数据复制到新的内存空间中。
class MyString {
public:
MyString& operator=(const MyString& other) {
if (this != &other) {
// 避免自我赋值
delete[] m_data; // 释放原有的内存空间
m_size = other.m_size;
m_data = new char[m_size + 1];
std::strcpy(m_data, other.m_data);
}
return *this; // 返回当前对象的引用
}
private:
char* m_data;
int m_size;
};
小组任务
// 拷贝赋值
list<T> &operator=(const list<T> <)
{
if (this != <) // 要 < 吗??? --->必须要!!! lt是一个普通的链表,而this是一个指针,所以要用取地址符&取出lt的地址,再和指针this里面存的地址作比较,如果相同,则说明调用该函数的A和原来的B是一样的,若不同,就要执行拷贝赋值操作
//因为是运算符重载,所以在用到 链表A=链表B 时(即需要将链表B赋值给链表A时), A是调用这个重载函数的发起方,所以该函数里的this指针指向的就是链表A,而链表B则作为函数的参数,通过引用&变成链表lt,在函数中以lt的形式存在,并通过一系列操作,将lt的每个节点的值都赋给this所指的那个链表(也就是链表A)
{
clear(); // 既然不相等,就先清空this所指向的链表,也就是链表A。但为什么clear函数里没有出现this? --->只是一般默认head_前面的this不写, 其实this->head_ 和 head_ 是等价的
for (auto it = lt.begin(); it != lt.end(); ++it)
{
push_back(*it);
}
}
return *this;
}
关系运算符重载
// 重载==
bool operator==(const iterator_ &t)
{
if (this->node_ptr_ == t.node_ptr_)
{
return true;
}
else
{
return false;
}
}
// 重载!=
bool operator!=(const iterator_ &t)
{
if (this->node_ptr_ == t.node_ptr_) // 这里也是用==
{
return false;
}
else
{
return true;
}
}
函数调用运算符重载
- 重载的是一个()—>括号
- 因为像函数,所以该重载又称为仿函数
- 代码的最后一行匿名对象的调用指的是不用创建一个新的函数就直接能用该重载的运算符
c++系列(持续更新中ing):
c++(引用、函数)
c++(类和对象_封装)
c++(类和对象_继承 / 多态)