【三】【C++】类与对象(二)

类的六个默认成员函数

在C++中,有六个默认成员函数,它们是编译器在需要的情况下自动生成的成员函数,如果你不显式地定义它们,编译器会自动提供默认实现。这些默认成员函数包括:

  1. 默认构造函数 (Default Constructor): 如果你没有为类显式定义任何构造函数,编译器将生成一个无参的默认构造函数。用于创建对象而不需要提供任何参数。例如:ClassName obj;

  2. 析构函数 (Destructor): 如果你没有为类定义析构函数,编译器将生成一个默认的析构函数。用于在对象被销毁时释放资源、进行清理工作等操作。例如:当对象超出作用域、被删除或者动态分配的对象被 delete 时。

  3. 拷贝构造函数 (Copy Constructor):(具有缺陷---浅拷贝 如果你没有为类定义拷贝构造函数,编译器将生成一个默认的拷贝构造函数。用于创建一个对象作为另一个对象的副本,通常在赋值、传递参数等情况下调用。例如:ClassName obj2 = obj1;

  4. 赋值运算符重载 (Copy Assignment Operator):(具有缺陷---浅拷贝 如果你没有为类定义赋值运算符重载函数,编译器将生成一个默认的赋值运算符重载函数。用于将一个对象的值复制给另一个对象,通常在赋值操作中调用。例如:obj2 = obj1;

  5. 移动构造函数 (Move Constructor)

  6. 移动赋值运算符 (Move Assignment Operator)

主要常见的是前四个,本篇文章只介绍前四个默认成员函数,最后两个有兴趣的小伙伴可以自己去查一下别的资料。

初始化操作

 
/*1.正常使用Data类初始化*/
#include <iostream>
using namespace std;
class Data {public:void Init(int year, int month, int day) {_year = year;_month = month;_day = day;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};
int main() {Data d1;d1.Init(2024, 1, 29);d1.ShowInfo();Data d2;d2.Init(2024, 1, 28);d2.ShowInfo();return 0;}

每当我们定义一个自定义类型,创造对象的时候,一定需要对其进行初始化操作。既然对于所有的对象都需要进行初始化操作,那么每次显示调用Init函数就显得非常冗余。如果每当我们创建一个对象,自动对其进行初始化,就省去显示调用Init函数的步骤。

构造函数

C++引入了一个新的概念,构造函数。构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象的整个生命周期内只调用一次。

 
/*2.使用构造函数进行初始化*/
#include <iostream>
using namespace std;
class Data {public:Data(int year, int month, int day) {_year = year;_month = month;_day = day;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};
int main() {Data d1(2024,1,29);d1.ShowInfo();Data d2(2024,1,28);d2.ShowInfo();return 0;}

这样我们每创建一个对象,编译器就会自动调用对应的构造函数对其进行初始化操作。也就是说构造函数是为了方便我们对实例化对象进行初始化而创造出来的。

构造函数的特性

构造函数是一种特殊的成员函数。它的特性是:

  1. 函数名与类名相同。

  2. 无返回值。

  3. 对象实例化时编译器自动调用对应的构造函数。

  4. 构造函数可以重载。

 
/*3.初步介绍构造函数*/
#include <iostream>
using namespace std;
class Data {
public:Data(){}Data(int year,int month,int day){_year=year;_month=month;_day=day;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;};
void TestDate(){Data d1;Data d2(2024,1,28);}
int main() {TestDate();return 0;}

探究构造函数的规则---创建对象自动调用构造函数

 
/*4.探究构造函数的规则---创建对象自动调用构造函数*/
#include <iostream>
using namespace std;
class Data {
public:Data(){cout<<"Data()"<<endl;}Data(int year,int month,int day){cout<<"Data(int year,int month,int day)"<<endl;_year=year;_month=month;_day=day;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;};
void TestDate(){Data d1;Data d2(2024,1,28);d2.ShowInfo();}
int main() {TestDate();return 0;}

当我们创建d1对象时,后面不加括号表示调用无参的构造函数。后面带括号,括号里带参数,表示调用有参的构造函数。创建对象的时候系统会自动调用对应的构造函数对其进行初始化操作。

探究构造函数的规则---编译器默认生成无参构造函数

 
/*5.探究构造函数的规则---编辑器默认有无参构造函数*/
#include <iostream>
using namespace std;
class Data {
public:void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;};
void TestDate(){Data d1;d1.ShowInfo();}
int main() {TestDate();return 0;}

当我们没有显示编写构造函数的时候,创建完对象后我们发现对象内年月日的初始值是0。

如果类中没有显示定义构造函数,编译器会自动生成一个无参的默认构造函数,但是一旦我们显示定义了构造函数,编译器就不会自动生成无参构造函数。

探究构造函数的规则---自己创建有参的构造函数时,编辑器不会自动创建无参构造函数

 
/*6.探究构造函数的规则---自己创建有参的构造函数时,编辑器不会自动创建无参构造函数*/
#include <iostream>
using namespace std;
class Data {
public:
//    Data(){//        cout<<"Data()"<<endl;//    }Data(int year,int month,int day){cout<<"Data(int year,int month,int day)"<<endl;_year=year;_month=month;_day=day;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;};
void TestDate(){Data d1;Data d2(2024,1,28);d2.ShowInfo();}
int main() {TestDate();return 0;}

我们显示定义了有参的构造函数,此时编译器不会自动生成无参的构造函数。当我们创建对象,后面不加括号的时候,系统会自动调用无参的构造函数,但是类类型中没有无参的构造函数,编译器没有生成,所以就报错了。解决的办法就是自己再写一个无参的构造函数。

探究构造函数的规则---默认构造函数具体情况---无参,全缺省

 
/*7.探究构造函数的规则---默认构造函数具体情况---无参,全缺省*/
#include <iostream>
using namespace std;
class Data {
public:Data(){cout<<"Data()"<<endl;_year=1999;_month=1;_day=1;}Data(int year=2024,int month=2,int day=1){cout<<"Data(int year,int month,int day)"<<endl;_year=year;_month=month;_day=day;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;};
void TestDate(){Data d1;d1.ShowInfo();}
int main() {TestDate();return 0;}

当我们没有显示的定义构造函数时,编译器会默认帮我们定义一个无参的构造函数。全缺省的构造函数等价于无参的构造函数。因此不可以同时存在,当我们创建对象后面不加括号时,系统调用无参的构造函数,此时编译器不知道调用无参的构造函数还是全缺省的构造函数。

问题产生的本质是缺省参数导致重载模棱两可,可以理解为缺省参数不存在,此时含有缺省参数的函数与另一个函数不构成重载,即使编译器没有报错。

 
/*7.缺省参数形成冲突*/
#include <iostream>
using namespace std;
class Data {public:Data(int year, int month = 2, int day = 1) {cout << "Data(int year,int month,int day)" << endl;_year = year;_month = month;_day = day;}Data(int year) {_year = year;_month = 1;_day = 1;}void ShowInfo() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};
void TestDate() {Data d1(2024);d1.ShowInfo();}
int main() {TestDate();return 0;}

当我们创建对象传入2024一个参数的时候,编译器不知道调用有缺省参数的函数还是没有缺省参数的函数,缺省参数导致重载模棱两可,可以理解为缺省参数不存在,即Data(int year, int month = 2, int day = 1) 等价于Data(int year) 与下面的 Data(int year)不构成重载,即使编译器不会报错。

清理操作

 
/*8.正常使用栈,进行清理工作*/
#include <iostream>
using namespace std;
typedef int DataType;
class Stack {public:Stack(int capacity = 4) {_array = (DataType*)malloc(sizeof(DataType) * capacity);_capacity = capacity;_size = 0;}void CheckCapacity(){if(_capacity==_size){int newcapacity=_capacity*2;_array=(DataType*)realloc(_array,sizeof(DataType)*newcapacity);_capacity=newcapacity;}}void push(DataType data) {CheckCapacity();_array[_size++] = data;}void pop() {_size--;}void Destroyed() {free(_array);_array = NULL;_capacity = 0;_size = 0;}bool empty() {return _size == 0;}DataType top() {return _array[_size - 1];}private:DataType* _array;int _capacity;int _size;};
void TestStack() {Stack st;st.push(1);st.push(2);st.push(3);st.push(4);st.push(5);while (!st.empty()) {cout << st.top() << endl;st.pop();}st.Destroyed();}
int main() {TestStack();return 0;}

当我们使用创建对象时,使用完之后都需要显示调用Destroyed销毁函数进行清理工作,防止内存泄漏。既然对于所有的对象都需要进行清理操作,那么每次显示调用Destroyed函数就显得非常冗余。如果每当我们创建一个对象,对象出了作用域之后自动对其进行销毁,就省去显示调用Destroyed函数的步骤。

析构函数

 
/*9.使用析构函数,进行清理工作*/
#include <iostream>
using namespace std;
typedef int DataType;
class Stack {public:Stack(int capacity = 4) {_array = (DataType*)malloc(sizeof(DataType) * capacity);_capacity = capacity;_size = 0;}void CheckCapacity() {if (_capacity == _size) {int newcapacity = _capacity * 2;_array = (DataType*)realloc(_array, sizeof(DataType) * newcapacity);_capacity = newcapacity;}}void push(DataType data) {CheckCapacity();_array[_size++] = data;}void pop() {_size--;}~Stack() {free(_array);_array = NULL;_capacity = 0;_size = 0;}bool empty() {return _size == 0;}DataType top() {return _array[_size - 1];}private:DataType* _array;int _capacity;int _size;};
void TestStack() {Stack st;st.push(1);st.push(2);st.push(3);st.push(4);st.push(5);while (!st.empty()) {cout << st.top() << endl;st.pop();}}
int main() {TestStack();return 0;}

C++引入了一个新的概念,析构函数。析构函数是一个特殊的成员函数,名字与类名相同,名字前面添加‘~’,类类型对象出作用域时由编译器自动调用。

析构函数的特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。

  2. 无参数无返回值类型。

  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。

  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

 
/*10.初步介绍析构函数*/
#include <iostream>
using namespace std;
class Date {public:Date(int year, int month, int day) {cout << "Date(int year, int month, int day)" << endl;_year = year;_month = month;_day = day;}void Show() {cout << _year << "-" << _month << "-" << _day << endl;}~Date() {cout << "~Date()" << endl;}private:int _year;int _month;int _day;};void Test() {Date d1(2024, 1, 29);d1.Show();}
int main() {Test();return 0;}

拷贝构造

 
/*11.正常用一个已有的变量拷贝构造一个新变量---内置类型*/
#include <iostream>
using namespace std;int main() {int a=10;int b=a;cout<<b<<endl;return 0;}

我们用已经存在的a变量去构造新的b变量,很明显如果a、b都是内置类型,我们很容易理解。但是如果我们希望用一个已经存在的自定义类型去构造新的自定义类型对象,应该如何操作?

 
/*12.正常用一个已有的变量拷贝构造一个新变量---自定义类型*/
#include <iostream>
using namespace std;
class Date {public:Date() {}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Show() {cout << _year << "-" << _month << "-" << _day << endl;}~Date() {cout << "~Date()" << endl;}void assignment(const Date& d) {_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;};void Test() {Date d1(2024, 1, 29);//d2=d1?Date d2;d2.assignment(d1);d2.Show();}
int main() {Test();return 0;}

我们可以在类里面定义一个函数,函数的参数是自定义类型的引用,作用是进行赋值,这样我们创建一个新对象后,调用这个对象的赋值函数,就可以实现我们想要的效果。我们可以发现对于每一个自定义类型来说,我们都希望可以这样实现"自定义类型 d2=d1",每个自定义类型都在类里面定义一个函数,外部调用会显得特别冗余,且外部调用形式是 Date d2; d2.assignment(d1); 先创建对象再进行赋值,而我们的期望是创建对象和赋值一体化,这两者似乎有所区别。

拷贝构造函数

 
/*12.使用拷贝构造函数拷贝构造一个新变量---自定义类型*/
#include <iostream>
using namespace std;
class Date {public:Date() {}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Show() {cout << _year << "-" << _month << "-" << _day << endl;}~Date() {cout << "~Date()" << endl;}Date(const Date& d) {_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;};void Test() {Date d1(2024, 1, 29);//d2=d1?Date d2(d1);d2.Show();}
int main() {Test();return 0;}

C++引入了一个新的概念,拷贝构造函数,只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

拷贝构造函数的特性

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。

  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

  3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

探究拷贝构造函数的规则---编译器默认生成拷贝构造函数

 
/*13.默认生成的拷贝构造函数*/
#include <iostream>
using namespace std;
class Date {public:Date() {}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Show() {cout << _year << "-" << _month << "-" << _day << endl;}~Date() {cout << "~Date()" << endl;}private:int _year;int _month;int _day;};void Test() {Date d1(2024, 1, 29);//d2=d1?Date d2(d1);d2.Show();}
int main() {Test();return 0;}

即使我们没有显示编写拷贝构造函数,我们依旧可以用Date d2(d1);拷贝构造d2对象。因为编译器会自动生成对应的拷贝构造函数。

探究拷贝构造函数的规则---编译器默认生成拷贝构造函数的缺陷(浅拷贝)

 
/*14.默认生成的拷贝构造函数---本质*/
#include <iostream>
using namespace std;
class f1 {public:f1(int x) {p = (int*)malloc(sizeof(int));*p = x;}void Show() {cout << *p << endl;cout << p << endl;}~f1() {if (p) {free(p);}}private:int* p;};void Test() {f1 x1(1);f1 x2(x1);x1.Show();x2.Show();}
int main() {Test();return 0;}

运行上面的代码我们会发现程序崩掉了。如果在28行打一个断点,进入调试模式,我们发现一直运行到28行处程序都没有发生问题。此时我们得到,

我们发现x1的地址和x2的地址是一样的。很容易知道接下来出Test函数后,x1和x2对象都需要调用析构函数,此时同一个地址会释放两次空间,程序就崩了。

编译器默认生成的拷贝构造函数按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。可以理解为浅拷贝是将自定义类型类内成员变量进行简单的等号赋值。即x2.p=x1.p;。此时指针就会指向同一段空间。而我们希望的是指针会新开辟一个空间,只是指针的值是相同的,空间不是相同的。

 
/*15.默认生成的拷贝构造函数---本质*/
#include <iostream>
using namespace std;
class f1 {public:f1(int x) {p = (int*)malloc(sizeof(int));*p = x;}void Show() {cout << p << endl;cout << *p << endl;}~f1() {if (p) {free(p);}}f1(const f1& x) {p = (int*)malloc(sizeof(int));*p = *x.p;}private:int* p;};void Test() {f1 x1(1);f1 x2(x1);x1.Show();x2.Show();}
int main() {Test();return 0;}

此时我们显示定义拷贝构造函数,新开辟空间,只是让指针上的值相等。这样就可以达到我们想要的效果,指针指向的空间不同,而指针上的值是相等的。

探究拷贝构造函数的规则---拷贝构造函数的使用场景(编译器优化、返回对象生命周期延长)

拷贝构造函数是C++中的一个特殊函数,用于创建一个新对象并将其初始化为另一个对象的副本。它通常在以下情况下被调用:

  1. 对象的初始化:当你创建一个新对象并将其初始化为另一个对象的副本时,拷贝构造函数被调用。

  2. 函数参数传递:当你将一个对象作为参数传递给一个函数时,拷贝构造函数可以被用来创建传递给函数的副本。

  3. 函数返回值:当函数返回一个对象的副本时,拷贝构造函数被用来创建返回的副本。(编译器优化可能省略)

 
/*16.拷贝构造函数---的使用场景*/
#include <iostream>
using namespace std;
class Date {public:Date(int year, int month, int day) {cout << "Date(int year,int month,int day):" << this << endl;}Date(const Date&d) {cout << "Date(const Date&d);" << this << endl;}~Date() {cout << "~Date():" << this << endl;}private:int _year;int _month;int _day;};
Date Test(Date d) {Date temp(d);return temp;}
int main() {Date d1(2024, 1, 29);Date  d2 =Test(d1);return 0;}

Date d1(2024, 1, 29);我们调用构造函数创建对象d1。Test(d1)此时调用Test函数,将d1传给形成d这一过程,调用拷贝构造函数,用已经存在的d1创建并初始化d对象。(函数参数传递:当你将一个对象作为参数传递给一个函数时,拷贝构造函数可以被用来创建传递给函数的副本。)在Test函数中,显示调用拷贝构造函数,用已经存在的d创建并初始化temp对象。(对象的初始化:当你创建一个新对象并将其初始化为另一个对象的副本时,拷贝构造函数被调用。在Test函数作用域结束,调用对象d的析构函数。在主函数作用域结束,调用temp的析构函数接着调用d2的析构函数。

你可能会产生疑问,在Test函数调用结束,temp作为函数返回值,此时应该还会调用拷贝构造函数。(函数返回值:当函数返回一个对象的副本时,拷贝构造函数被用来创建返回的副本。)为何运行代码并没有显示拷贝构造函数被调用。

实际上temp作为返回值,此时还会调用拷贝构造函数,但是经过编译器优化过,这一步的拷贝构造函数被省略掉了。尽管你在 Test 函数中创建了一个 temp 对象并返回它,但在实际的编译过程中,编译器会进行优化,避免调用拷贝构造函数,直接将 temp 对象的值传递给 d2。这是一种性能优化,可以减少不必要的对象拷贝操作,提高程序效率。

我对代码进行调试,注意看我的光标的位置,我的光标处于主函数中,但是析构函数只调用了一次,显然这个析构函数是对Test中d对象进行析构,而temp和d2对象并没有发生析构。显然temp在Test函数中作为返回值,此时temp对象的生命周期得到延伸,与d2同步,但最后编译器会依照前后顺序先调用temp的析构函数再调用d2的析构函数。虽然拷贝构造函数被省略了,但是 temp 对象的生命周期和 d2 对象的初始化完全一致,因此 temp 对象的析构函数会在 d2 对象的析构函数之前被调用,从而保证资源的正确释放。

运算符重载

运算符重载(Operator Overloading)是C++的一个重要特性,允许你为用户自定义的数据类型定义和重新定义运算符的行为。通过运算符重载,你可以使用C++内置运算符来执行自定义数据类型的操作,使代码更具可读性和表现力。

内置类型判断相等很简单,例如a,b变量都是int类型,判断相等只需要a==b?即可。但是如果我们想要定义自定义类型进行判断是否相等。我们希望也可以像“a==b”这样使用,对于ab都属于自定义类型,此时编译器显然不能像内置类型那样操作。于是我们引入运算符重载的概念,使得自定义类型也可以像内置类型那样进行操作。

 
/*17.运算符重载*/
#include <iostream>
using namespace std;
class Date {
public:
Date(int year,int month,int day){_year=year;_month=month;_day=day;}
bool operator==(const Date& d2){return _year==d2._year&& _month==d2._month&& _day==d2._day;}
private:int _year;int _month;int _day;};int main() {Date d1(2024,1,29);Date d2(d1);cout<<(d1==d2)<<endl;return 0;}

当我们使用d1==d2这段代码的时候,实际上会转化为d1.operator==(d2) 此时调用d1对象的operator==函数,并传入d2对象作为参数。此时d1的地址会作为this指针,operator==函数会隐藏一个this指针,this指针指向的地址是d1的地址,_year等价于this->_year,_month等价于this->_month,_day等价于this->_day。

赋值运算符重载

如果我们想要以这种形式“a=b”对一个自定义类型进行赋值,那么我们就需要对“=”进行运算符重载。

赋值运算符重载格式:

参数类型:const T&,传递引用可以提高传参效率

返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值

返回*this :要符合连续赋值的含义

 
/*18.赋值运算符重载*/
#include <iostream>
using namespace std;
class Date {public:Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date& operator=(const Date&d) {if (this != &d) {_year = d._year;_month = d._month;_day = d._day;}return *this;}void Show() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};int main() {Date d1(2024, 1, 29);Date d2 = d1;d2.Show();return 0;}

实际上当我们没有显示定义赋值运算符重载的时候,编译器会生成一个默认的赋值运算符重载,以值的方式逐字节拷贝,即浅拷贝。我们知道浅拷贝当成员变量是指针的时候,p1=p2此时指针指向的地址会相同,而我们希望的是指针指向的地址不同,指针指向地址上的值相同,因为往往涉及到指针问题我们都需要显示定义赋值运算符重载,将浅拷贝变成深拷贝。

默认赋值运算符重载的缺陷

 
/*19.默认赋值运算符重载的缺陷*/
#include <iostream>
using namespace std;
class f1 {public:f1(int x) {p = (int*)malloc(sizeof(int));*p = x;}void Show() {cout << *p << endl;cout << p << endl;}~f1() {if (p) {free(p);}}private:int* p;};void Test() {f1 x1(1);f1 x2 = x1;x1.Show();x2.Show();}
int main() {Test();return 0;}

x1,x2对象中p成员指向的地址是相同的,当x1,x2对象出作用域时,会对同一块地址调用两次析构函数,此时程序就会崩掉。此时就需要显示定义赋值运算符重载,将浅拷贝变成深拷贝。

前置++和后置++的重载

 
/*20.前置++和后置++的重载(代码逻辑有缺陷)*/
#include <iostream>
using namespace std;
class Date {public:Date() {}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date& operator++() {_day += 1;return *this;}Date operator++(int) {Date temp(*this);_day++;return temp;}void Show() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;};
void Test() {Date d;Date d1(2024, 1, 29);d = d1++;d.Show();d1.Show();d = ++d1;d.Show();d1.Show();}
int main() {Test();return 0;}

 
Date operator++(int) {Date temp(*this);_day++;return temp;}

这是后置++,为了区分前置++和后置++,规定后置++参数中写一个int,用来表示后置++。

前置++先对day进行+1操作,然后把处理过的自定义类型返回,此时返回引用,用引用可以提高效率,因为不用引用还需要进行一次拷贝构造操作,用引用就不需要进行拷贝构造操作。

后缀++返回的值是++前的值,因此我们先拷贝构造一个副本,对本体的day进行++操作,返回副本的值,注意此时不能够返回引用,因为副本出了作用域就消失了,所以只能不传引用。

d1++会被转化为d1.operator++(int),从而调用后置++的函数。++d1会被转化为d1.operator++(),从而调用前置++的函数。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

C++ 数论相关题目 博弈论:拆分-Nim游戏

给定 n 堆石子&#xff0c;两位玩家轮流操作&#xff0c;每次操作可以取走其中的一堆石子&#xff0c;然后放入两堆规模更小的石子&#xff08;新堆规模可以为 0 &#xff0c;且两个新堆的石子总数可以大于取走的那堆石子数&#xff09;&#xff0c;最后无法进行操作的人视为失…

PMP中的数据收集工具:打开项目成功的钥匙

在项目管理中&#xff0c;数据收集是关键的一环。准确、及时的数据能够为项目决策提供可靠的依据&#xff0c;帮助项目经理更好地监控项目进展、识别潜在风险&#xff0c;并制定有效的应对措施。本文将深入探讨PMP&#xff08;项目管理专业&#xff09;中常用的数据收集工具&am…

力扣题目训练(6)

2024年1月30日力扣题目训练 2024年1月30日力扣题目训练367. 有效的完全平方数374. 猜数字大小383. 赎金信99. 恢复二叉搜索树105. 从前序与中序遍历序列构造二叉树51. N 皇后 2024年1月30日力扣题目训练 2024年1月30日第六天编程训练&#xff0c;今天主要是进行一些题训练&…

在ubuntu上在安装Squid代理服务器

Squid 是一个代理和缓存服务器&#xff0c;它将请求转发到所需的目的地&#xff0c;同时保存请求的内容&#xff0c;当你再次请求相同内容时&#xff0c;他可以向你提供缓冲内容&#xff0c;从而提高访问速度。Squid代理服务器目前支持的协议有&#xff1a;http、SSL、DNS、FTP…

App测试中ios和Android有哪些区别呢?

App测试中&#xff0c;大家最常问到的问题就是&#xff1a;ios和 Android有什么区别呢&#xff1f; 在Android端&#xff0c;我们经常会使用 JavaScript、 HTML、 CSS等技术来编写一些简单的 UI界面。而 iOS端&#xff0c;我们经常会使用到 UI设计、界面布局、代码结构、 API等…

C++——特殊类

特殊类 文章目录 特殊类一、请设计一个类&#xff0c;不能被拷贝二、请设计一个类&#xff0c;只能在堆上创建对象方案一&#xff1a;析构函数私有化方案二&#xff1a;构造函数私有化 三、请设计一个类&#xff0c;只能在栈上创建对象四、请设计一个类&#xff0c;不能被继承五…

微软Office Plus与WPS Office的较量:办公软件市场将迎来巨变?

微软Office Plus在功能表现上远超WPS Office&#xff1f; 微软出品的Office套件实力强劲&#xff0c;其不仅在办公场景中扮演着不可或缺的角色&#xff0c;为用户带来高效便捷的体验&#xff0c;而且在娱乐生活管理等多元领域中同样展现出了卓越的应用价值 作为中国本土办公软…

Leetcode 第 381 场周赛题解

Leetcode 第 381 场周赛题解 Leetcode 第 381 场周赛题解题目1&#xff1a;3014. 输入单词需要的最少按键次数 I思路代码复杂度分析 题目2&#xff1a;3015. 按距离统计房屋对数目 I思路代码复杂度分析 题目3&#xff1a;3016. 输入单词需要的最少按键次数 II思路代码复杂度分析…

HarmonyOS使用Web组件加载页面

1、加载网络页面 在Web组件创建时&#xff0c;指定默认加载的网络页面 。在默认页面加载完成后&#xff0c;如果开发者需要变更此Web组件显示的网络页面&#xff0c;可以通过调用loadUrl()接口加载指定的网页。 默认在Web组件加载完“www.baidu.com”页面后&#xff0c;点击按…

html+js+css静态故宫主题

登录代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>登录 - 故宫博物院</title><…

C语言菜鸟入门·函数

目录 1. 函数的定义 2. 函数声明 3. 函数调用 4. 函数参数 4.1 传值调用 4.2 引用调用 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;即主函数 main() &#xff0c;所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同…

【C++】C++入门—— 引用

引用 1 前情提要2 概念剖析3 引用特性4 常引用5 使用场景5.1做参数5.2 做返回值 6 传值 传引用的效率比较7 引用与指针的差异Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读下一篇文章见 1 前情提要 在C语言中&#xff0c;我们往往会遇见复杂的指针&#xff08;如…

论文阅读-MapReduce

论文名称&#xff1a;MapReduce: Simplified Data Processing on Large Clusters 翻译的效果不是很好&#xff0c;有空再看一遍&#xff0c;参照一下别人翻译的。 MapReduce:Simplified Data Processing on Large Clusters 中文翻译版(转) - 阿洒 - 博客园 (cnblogs.com) 概…

C#,欧拉数(Eulerian Number)的算法与源代码

1 欧拉数 欧拉数特指 Eulerian Number&#xff0c;不同于 Euler numbers&#xff0c;Eulers number 哦。 组合数学中&#xff0c;欧拉数&#xff08;Eulerian Number&#xff09;是从1到n中正好满足m个元素大于前一个元素&#xff08;具有m个“上升”的排列&#xff09;条件的…

mini-spring 实现应用上下文,自动识别、资源加载、扩展机制

我们不能让面向 Spring 本身开发的 DefaultListableBeanFactory 服务&#xff0c;直接给予用户使用 DefaultListableBeanFactory、XmlBeanDefinitionReader&#xff0c;是我们在目前 Spring 框架中对于服务功能测试的使用方式&#xff0c;它能很好的体现出 Spring 是如何对 xm…

【靶场实战】Pikachu靶场暴力破解关卡详解

Nx01 系统介绍 Pikachu是一个带有漏洞的Web应用系统&#xff0c;在这里包含了常见的web安全漏洞。 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习&#xff0c;那么Pikachu可能正合你意。 Nx02 Burte Force概述 “暴力破解”是一攻击具手段&#xff0c;在web…

Transformer 自然语言处理(二)

原文&#xff1a;Natural Language Processing with Transformers 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第五章&#xff1a;文本生成 基于 Transformer 的语言模型最令人不安的特点之一是它们生成的文本几乎无法与人类写的文本区分开。一个著名的例子是 Ope…

60、Flink CDC 入门介绍及Streaming ELT示例(同步Mysql数据库数据到Elasticsearch)-完整版

Flink 系列文章 一、Flink 专栏 Flink 专栏系统介绍某一知识点&#xff0c;并辅以具体的示例进行说明。 1、Flink 部署系列 本部分介绍Flink的部署、配置相关基础内容。 2、Flink基础系列 本部分介绍Flink 的基础部分&#xff0c;比如术语、架构、编程模型、编程指南、基本的…

【Docker】在Windows下使用Docker Desktop创建nginx容器并访问默认网站

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

布局技巧及CSS初始化

一&#xff0c;margin负值巧妙应用 二&#xff0c;文字围绕浮动元素 三&#xff0c;行内块 四&#xff0c;CSS三角强化 五&#xff0c;CSS初始化 一&#xff0c;margin负值巧妙应用 制作盒子的细线边框&#xff1a; 鼠标经过li后变色&#xff1a; 二&#xff0c;文字围绕…