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

【三】【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++(),从而调用前置++的函数。

结尾

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

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

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

相关文章:

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

类的六个默认成员函数 在C中&#xff0c;有六个默认成员函数&#xff0c;它们是编译器在需要的情况下自动生成的成员函数&#xff0c;如果你不显式地定义它们&#xff0c;编译器会自动提供默认实现。这些默认成员函数包括&#xff1a; 默认构造函数 (Default Constructor)&…...

ffmpeg 输入文件,输入出udp-ts 指定pid

要使用FFmpeg将输入文件转换为UDP传输流&#xff08;TS&#xff09;并指定特定的PID&#xff0c;您可以使用以下命令&#xff1a; ffmpeg -i input_file -c:v libx264 -preset ultrafast -tune zerolatency -f mpegts -map 0:v:0 -map 0:a:0 -pid 0x12345678 udp://output_addr…...

自研人工智能小工具-小蜜蜂(国外ChatGpt的平替)

国内有非常多好用的人工智能工具&#xff0c;但均无法完全替代国外ChatGpt。 ChatGPT相较于其他国内工具的优势在于以下几点&#xff1a; 创新的语言生成能力&#xff1a;ChatGPT是由OpenAI开发的先进的自然语言生成模型&#xff0c;它采用了大规模的预训练和精细调整方法。因此…...

Stable Diffusion 模型下载:ReV Animated

模型介绍 该模型能够创建 2.5D 类图像生成。此模型是检查点合并&#xff0c;这意味着它是其他模型的产物&#xff0c;以创建从原始模型派生的产品。 条目内容类型大模型基础模型SD 1.5来源CIVITAI作者s6yx文件名称revAnimated_v122EOL.safetensors文件大小5.13GB 生成案例 …...

某赛通电子文档安全管理系统 PolicyAjax SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…...

Prometheus 采集Oracle监控数据

前言 oracledb_exporter是一个开源的Prometheus Exporter,用于从Oracle数据库中收集关键指标并将其暴露给Prometheus进行监控和告警。它可以将Oracle数据库的性能指标转换为Prometheus所需的格式,并提供一些默认的查询和指标。 download Oracle Oracle Windows Install …...

【ARM Trace32(劳特巴赫) 使用介绍 3.1 -- 不 attach core 直接访问 memory】

文章目录 背景介绍背景介绍 在使用 trace32 时在有些场景需要不 attach core 然后去读写 memory,比如在某些情况下 core 已经挂死连接不上了,这个时候需要dump内存,这个时候需要怎做呢? print "test for memory access directly";SYStem.OPTION WAITRESET OF…...

MySQL事务和SQL优化

目录 一、什么是事务 二、事务的特征 三、MySQL使用事务 3.1 实现流程&#xff1a; 实现截图&#xff1a; 3.2 实例演示&#xff1a; 四、事务的隔离级别 幻读&#xff1a; 如何解决&#xff1a; 脏读&#xff1a; 不可重复读&#xff1a; 幻读和不可重复读两者区别…...

[C语言]结构体初识

结构体定义 结构体是一些值的集合,被成为成员变量,结构的每个成员可以是不同类型的变量 声明: 定义了一个结构体比如以张蓝图,不占据内存,当你创建了一个结构体变量时,才占空间. #include<stdio.h>//struct 为结构体关键字, student 自定义结构体名称 struct student …...

跨平台开发:浅析uni-app及其他主流APP开发方式

随着智能手机的普及&#xff0c;移动应用程序&#xff08;APP&#xff09;的需求不断增长。开发一款优秀的APP&#xff0c;不仅需要考虑功能和用户体验&#xff0c;还需要选择一种适合的开发方式。随着技术的发展&#xff0c;目前有多种主流的APP开发方式可供选择&#xff0c;其…...

MyBatis常见面试题汇总

说一下MyBatis执行流程&#xff1f; MyBatis是一款优秀的基于Java的持久层框架&#xff0c;它内部封装了JDBC&#xff0c;使开发者只需要关注SQL语句本身&#xff0c;而不需要花费精力去处理加载驱动、创建连接等的过程&#xff0c;MyBatis的执行流程如下&#xff1a; 加载配…...

juc并发线程学习笔记(一)

本系列会更新我在学习juc时的笔记和自己的一些思想记录。如有问题欢迎联系。 并发编程 进程与线程 1.进程和线程的概念 程序是静态的&#xff0c;进程是动态的 进程 程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载…...

力扣热门100题刷题笔记 - 3.无重复字符的最长子串

力扣热门100题 - 3.无重复字符的最长子串 题目链接&#xff1a;3. 无重复字符的最长子串 题目描述&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。示例&#xff1a; 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字…...

达梦数据库死锁排查与解决

在达梦数据库系统中&#xff0c;死锁是指两个或多个事务相互等待对方释放资源&#xff0c;从而造成循环等待的现象&#xff0c;严重影响数据库的正常运行。以下是使用达梦数据库进行死锁排查和解决的具体步骤&#xff1a; 死锁查看 查询当前死锁信息 SELECT lc.lmode, lc.ta…...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之TextClock组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之TextClock组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、TextClock组件 TextClock组件通过文本将当前系统时间显示在设备上。支持不同…...

CICD注册和使用gitlab-runner常见问题

1、现象 fatal: unable to access https://github.com/homebrew/brew/: 2、解决 git config --global --unset http.proxy git config --global --unset https.proxy 查看gitlab-runner是否成功&#xff1a; userusers-MacBook-Pro ~ % gitlab-runner -h 查看gitlab-run…...

关于Django部署

首先了解一下开发环境服务器跟生产环境服务器有何不同。 一、我们通过 python manage.py runserver 启动开发环境服务器&#xff0c;这条命令背后做了哪些事情&#xff1f; 1、首先加载Django项目的设置&#xff08;settings&#xff09; 2、检查数据库迁移&#xff0c;确保数…...

计算机网络——01什么是InterNet

什么是InterNet 1.1 什么是网络 由节点和边组成的与形状大小无关的拓扑 1.2 什么是Internet 从具体构成角度来说&#xff1a; 节点&#xff1a; 主机及其上运行的应用程序路由器、交换机等网络交换设备 边&#xff1a;通信链路 接入网链路&#xff1a;主机连接到互联网的链…...

刷存在感,Excel转Protobuf/Json通用配置文件

使用场景 最近工作流中有将Excel转Protobuf作为配置文件的技术方案。具体实现是先定一个proto文件&#xff0c;再在一个对应excel表中定义对应字段&#xff0c;由策划在excel进行更改。proto文件可以生成对应语言的脚本&#xff0c;然后将excel转成对应protobuf的binary。 我…...

docker 开放tcp连接供idea等其他外部工具开放使用

docker 开放tcp连接供idea等其他外部工具开放使用 方法一&#xff1a;通过systemd工具 sudo systemctl edit docker.service 修改文件内容如下 ExecStart/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 重启 systemctl 配置 sudo systemctl daemon-reload 重启docker服务 s…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...