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

C++:类和对象4

一,日期类实现

学习建议:

对于计算机学习来说,调试十分重要,所以在日常学习中一定要加大代码练习,刷代码题和课后自己敲出课上代码例题,注意不要去对比正确代码或者网上找正确代码直接使用,一定要自己去调试更改。

调试技巧:1,通过不断改变运算对象改变调试范围(学会缩小和放大范围),判断可能出现错误的程序范围

                   2,当不知道问题出现在哪的时候,对于一个对象用监视一步一步走,看该对象的变化,再对比自己的计算,判断出那块代码出现问题

还有很多调试技巧需要自己去理解总结

Date.h

#pragma once#include<iostream>
using namespace std;
#include<assert.h>class Date
{// 友元函数声明 friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 直接定义类⾥⾯,他默认是inline // 只有频繁调⽤的小函数在h文件中定义//得到月份所对应的天数int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))//闰年的二月份是20天的判断+返回{// 2return 29;}else{return monthDayArray[month];}}//d1+=天数Date& operator+=(int day);Date operator+(int day);//d1-=天数Date& operator-=(int day);Date operator-(int day);// ++d1 ->d1.operator++()  前置++Date& operator++();// d1++ ->d1.operator++(1) 后置++  为了区分,构成重载,给后置++,强⾏增加了⼀个int形参 // 这⾥不需要写形参名,因为接收值是多少不重要,也不需要⽤ // 这个参数仅仅是为了跟前置++构成重载区分 Date operator++(int);//--d1 前置--Date& operator--();//d1-- 后置--Date operator--(int);//大于 大于等于 小于 小于等于 等于 不等于bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);// d1-d2int operator-(const Date& d); // 流插⼊ // 不建议,因为Date* this占据了⼀个参数位置,使⽤d<<cout不符合习惯 //void operator<<(ostream& out);//打印void Print();
private:int _year;int _month;int _day;
};//重载
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

Date.cpp

#include"Date.h"bool Date::CheckDate()//检查日期是否正确·
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month)){return false;//天数,月数,年数不对时直接报错}else{return true;}
}Date::Date(int year, int month, int day)//对日期赋值
{_year = year;_month = month;_day = day;if (!CheckDate()){cout << "⽇期⾮法" << endl;}
}void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}// d1 < d2
bool Date::operator<(const Date& d) 
{if (_year < d._year)//先对年份进行判断{return true;}else if (_year == d._year){if (_month < d._month)//年份相同再对月份进行判断{return true;}else if (_month == d._month)//月份相同时则d1<d2{return _day < d._day;}}return false;
}// d1 <= d2
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;//直接借用上述的 < 运算符重载
}//同理
//d1>d2
bool Date::operator>(const Date& d) 
{return !(*this <= d);//直接利用上述小于等于运算符取逆
}//d1>=d2
bool Date::operator>=(const Date& d) 
{return !(*this < d);
}//d1==d2
bool Date::operator==(const Date& d) 
{return _year == d._year&& _month == d._month&& _day == d._day;
}//d1!=d2
bool Date::operator!=(const Date& d) const
{return !(*this == d);
}//d1+=天数
Date& Date::operator+=(int day)//重载运算函数是成员函数,,第一个参数是this指针,参数比运算对象少一个
{if (day < 0)// 当要加的数为负数时要求借用减法完成{return *this -= -day;}_day += day;//完成加法运算while (_day > GetMonthDay(_year, _month))//天数超越了对应月份的天数{_day -= GetMonthDay(_year, _month);//天数减去对应月份的天数++_month;//同时月份跳转到下一个月if (_month == 13)//当月份跳转到13时进入下一年,同时月份重新定义为1{_year++;_month = 1;}}return *this;//返回第一个运算对象
}//d1+天数
Date Date::operator+(int day)//完成加法运算
{Date tmp(*this);//拷贝一份*this用拷贝的tmp完成+=运算tmp += day;return tmp;
}//d1-=天数
Date& Date::operator-=(int day)//同理,重载运算函数是成员函数
{if (day < 0)//减去的数为负数的情况{return *this += -day;}_day -= day;//减法运算while (_day <= 0)//天数小于等于0{--_month;//月份-1if (_month = 0)//当月份为0时,跳转到上一年,同时月份重新定义为12{_year--;_month = 12;}_day += GetMonthDay(_year, _month); //  不断借助上一个月天数,使得负_day绝对值变小,直至不满足循环条件 }return *this;//返回自身
}//d1-天数
Date Date::operator-(int day)
{Date tmp(*this);//自身不能改变,拷贝,借助拷贝对象改变返回tmp -= day;return tmp;
}//++d1
Date& Date::operator++()
{*this += 1;return *this;
}//d1++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}//--d1
Date& Date::operator--()
{*this -= 1;return *this;
}//d1--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}//d1-d2
int Date::operator-(const Date& d) 
{int flag = 1;//假设法Date max = *this;//假设第一个大,第二个小Date min = d;if (*this < d)//假设错了就交换{max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;//借助n计算相差个数}return n * flag;//flag就是在两个数大小不同情况下确定运算是正还是负
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{cout << "请依次输⼊年⽉⽇:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "⽇期⾮法" << endl;}return in;
}

d1-d2的实现思路:

思路一:

1,小的月和天,朝大的对齐,算出来差了多少天

2,年直接减,算出来差多少天

例子:2025.3.11  和2020.5.1

先将3.11向5.1对齐发现三月十一到五月一号差了17+30+1天,此时为2025.5.1和2020.5.1相差五年再看其中闰年,从而计算相差天数再减去48天

思路二:直接赋用

将小的日期不断++直到与大的一致,算出来++了多少次。

上述代码就是借助思路二实现的

Test.cpp

#include"Date.h"
void TestDate1()
{// 这⾥需要测试⼀下⼤的数据+和- Date d1(2024, 4, 14);Date d2 = d1 + 30000;d1.Print();d2.Print();Date d3(2024, 4, 14);Date d4 = d3 - 5000;d3.Print();d4.Print();Date d5(2024, 4, 14);d5 += -5000;d5.Print();
}void TestDate2()
{Date d1(2024, 4, 14);Date d2 = ++d1;d1.Print();d2.Print();Date d3 = d1++;d1.Print();d3.Print();d1.operator++(1);d1.operator++(100);d1.operator++(0);d1.Print();
}void TestDate3()
{Date d1(2024, 4, 14);Date d2(2034, 4, 14);int n = d1 - d2;cout << n << endl;n = d2 - d1;
}void TestDate4()
{Date d1(2024, 4, 14);Date d2 = d1 + 30000;// operator<<(cout, d1)cout << d1;cout << d2;cin >> d1 >> d2;cout << d1 << d2;
}
void TestDate5()
{const Date d1(2024, 4, 14);d1.Print();//d1 += 100;d1 + 100;Date d2(2024, 4, 25);d2.Print();d2 += 100;d1 < d2;d2 < d1;
}int main()
{return 0;
}

二,取地址运算符重载

1,const成员函数

(1)将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后 ⾯。

(2)const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。 const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this

在上述的成员函数后面都可以加上const。

下面例子出现权限放大:调用print函数需要传递this指针,也就是&d1,而this指针这里相当于Date const this

而上面的const修饰的相当于const Date*,传递过去时会发生权限放大,报错

例子2:

const Date*传递过去会发生权限的平移

Date*传递过去发生权限缩小

2,取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动 ⽣成的就可以够我们⽤了,不需要去显⽰实现。除⾮⼀些很特殊的场景,⽐如我们不想让别⼈取到当前类对象的地址,就可以⾃⼰实现⼀份,胡乱返回⼀个地址。

class Date
{ 
public :Date* operator&(){return this;// return nullptr;//当不想让别人取到地址,可以返回nullptr或者return (Date*)0x112245EF 返回一个假地址}const Date* operator&()const{return this;// return nullptr;}
private :int _year ; // 年 int _month ; // ⽉ int _day ; // ⽇ 
};

三,再探构造函数

(1)之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅ 式,就是初始化列表,初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成 员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式

class Date
{
public://初始化列表Date(int& x, int year = 1, int month = 1, int day = 1):_year(year), _month(month), _n(10),_ref(x)
//可以混合使用两者初始化方法{_day = day;_n=1;//不可行,初始化列表的变量只能初始化一次}private:int _year;int _month;int _day;// 必须在初始化列表初始化的成员const int _n;//常量初始化int& _ref;//引用初始化};

(2)每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义 初始化的地⽅。

(3)引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始 化,否则会编译报错。

class Date
{
public:Date(int& x, int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day),_t(12),_ref(x),_n(1){// error C2512: “Time”: 没有合适的默认构造函数可⽤ // error C2530 : “Date::_ref” : 必须初始化引⽤ // error C2789 : “Date::_n” : 必须初始化常量限定类型的对象 }void Print() const
{cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;Time _t; // 没有默认构造 int& _ref; // 引⽤ const int _n; // const 
};

(4)C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的 成员使⽤的。

class Time
{
public:Time(int hour):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};class Date
{
public:Date():_month(2){cout << "Date()" << endl;}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}private:// 注意这里不是初始化,这里给的是缺省值,这个缺省值是给初始化列表的// 如果初始化列表没有显示初始化,默认就会用这个缺省值初始化int _year = 1;//year没有在列表中初始化,但是通过了缺省值进行初始化。int _month = 1;int _day;const int _n = 1;Time _t = 10;int* _ptr = (int*)malloc(12);//缺省值也可以是一个表达式,其实只要是一个值就行
};class Stack
{
public:Stack(int n = 4){cout << "Stack(int n)" << endl;}
private://...
};class MyQueue
{
public:MyQueue(int n);_pushst(n),popost(n){}private:// 声明Stack _pushst;//自定义类型没有默认构造,需要显示的写构造函数Stack _popst;const int _n = 1; // 缺省值初始化
};
int main()
{Date d1;MyQueue q;//调用初始化构造函数return 0;
}

(5)尽量使⽤初始化列表初始化,因为那些你不在初始化列表初始化的成员也会⾛初始化列表,如果这 个成员在声明位置给了缺省值,初始化列表会⽤这个缺省值初始化。如果你没有给缺省值,对于没 有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有 显⽰在初始化列表初始化的⾃定义类型成员会调⽤这个成员类型的默认构造函数,如果没有默认构 造会编译错误。

(6)初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆ 关。建议声明顺序和初始化列表顺序保持⼀致。

初始化列表:

⽆论是否显⽰写初始化列表,每个构造函数都有初始化列表;

⽆论是否在初始化列表显⽰初始化成员变量,每个成员变量都要⾛初始化列表初始化;

练习题:

#include<iostream>
using namespace std;
class A
{public:A(int a):_a1(a), _a2(_a1)//初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆关{}void Print(){cout << _a1 << " " << _a2 << endl;}private:int _a2 = 2;//列表中已经进行了显式的初始化,所以这里与缺省值无关了int _a1 = 2;
};int main()
{A aa(1);aa.Print();
}

先用a1初始化a2,但a1未进行初始化,所以a1是一个随机值,a2就是一个随机值。

再用a=1初始化a1,a1就是1

上述程序的运行结果是:输出1和随机值

四,类型转换

(1)C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。

不同编译器是否支持优化是不一样的。

(2)构造函数前⾯加explicit就不再⽀持隐式类型转换。

(3)类类型的对象之间也可以隐式转换,需要相应的构造函数⽀持。

class A
{
public:// 构造函数explicit就不再支持隐式类型转换// explicit A(int a1)A(int a1):_a1(a1){cout << "A(int a1)" << endl;}A(const A& aa){cout << "A(const A& aa)" << endl;}//explicit A(int a1, int a2)A(int a1, int a2):_a1(a1), _a2(a2){}void Print(){cout << _a1 << " " << _a2 << endl;}int Get() const{return _a1 + _a2;}
private:int _a1 = 1;int _a2 = 2;
};//自定义类型之间也可以进行类型转换
class B
{
public:B(const A& a):_b(a.Get()){}
private:int _b = 0;
};class Stack
{
public:void Push(const A& aa)//临时对象有常性{}void Push(const B& bb){}
};int main()
{// 构造A aa1(1);// 隐式类型转换// 1构造临时对象,临时对象拷贝构造aa2,优化后直接构造// 1构造⼀个A的临时对象,再⽤这个临时对象拷⻉构造aa3 // 编译器遇到连续构造+拷⻉构造->优化为直接构造 A aa2 = 1;A& aa3 = aa2;//前面加上一个const时为普通引用,不可行,因为临时对象具有常性const A& aa4 = 1;Stack st;A aa10(10);st.Push(aa10);//原来需要定义一个aa10对象,拷贝构造给Push函数,再进行push操作//而类型转换可以直接创建临时变量进行拷贝构造,进行优化,传参st.Push(10); // 隐式类型转换支持这样的间接传参// C++11之后才⽀持多参数转化 A aa3 = { 2,2 };A aa5(1, 1);// 隐式类型转换// { 1, 1 }构造临时对象,临时对象拷贝构造aa5,优化后直接构造A aa6 = { 1, 1 };A aa11(11, 11);st.Push(aa11);st.Push({ 11, 11 }); // 隐式类型转换支持这样的间接传参B b1(aa1);//自定义类型转换为自定义类型// 隐式类型转换B b1 = aa1;B b3(aa1);st.Push(b3);st.Push(aa1);return 0;
}

当优化关闭后,会出现构造+拷贝构造

五,static成员

static:

(1)⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化

(2)静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区,生命周期是全局的。因此对象大小不包括静态成员

(3)⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针

(4)静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针

(5)⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数

(6)突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量 和静态成员函数

(7)静态成员也是类的成员,受public、protected、private访问限定符的限制

(8)静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员 变量不属于某个对象,不⾛构造函数初始化列表。

声明:

private:// 类⾥⾯声明 static int _scount;

初始化:

// 类外⾯初始化 
int A::_scount = 0;

计算程序中创建了多少个类的对象

通过count看调用了多少次构造函数

静态成员变量不走初始化列表,不能给缺省值

通过GetACount公有的成员函数对私有的静态成员变量进行访问。

静态成员变量是公有时的访问方式

1,静态成员变量属于所有对象,可以通过指定对象来访问。

count<<a1._scount<<endl;

2,可以指定类域进行访问。

count<<A::_scount<<endl;

注意:静态成员函数无法对非静态成员进行访问

原因:(1)静态成员没有this指针

(2)而非静态成员可以通过类域访问

题目1:

思路:通过Solution成员函数构建对象,从而去调用Sum类域中的构造函数,构造对象大小有多大就调用多少次,并且是静态成员变量,所以全局调用,每次使用的ret和i都是上次变化后的,来实现求和。

一般刷题网站都是支持变长数组的,但VS是不支持的

VS中更改:

Sum*ptr=new Sum[n];
delete[]ptr;//动态开辟
#include<iostream>
using namespace std;
class A
{
public:A(){++_scount;}
private://.......
};int main()
{A a1, a2;A a3(a1);//定义一个对象调用一次构造A aa1[10];//一次性定义多个对象,调用多次构造函数return 0;
}

题目2:

构造和析构顺序

构造:全局变量在main函数之前创建,调用其构造函数,C最先调用,而main函数中静态成员变量只要在使用时才会构造,所以先调用A,B的构造。同样,对于多次调用有静态变量函数只会调用一次构造函数,因为只会初始化一次。

析构:main函数栈帧先销毁,先销毁局部变量,后定义的先析构,对于全局变量(可以通过监视地址,进入析构函数监视this指针观察)发现静态成员变量先析构。

六,友元

1,流插入和提取的运算符重载

void TestDate6()
{Date d1(2025,3,12);printf("%");
}

对于printf和scanf只能打印内置类型,而对于自定义类型无法进行打印,C++为了解决这个问题,设计了流插入和流提取。

void TestDate6()
{int i = 22;double d = 1.2;cout << i;cout.operator<<(i);//运算符重载 相当于调用运算符重载调用函数,自动识别类型是因为重载机制,自动调用对应类型的重载cout << endl;cout << d;cout.operator<<(d);cout << i << " " << d << endl;*/
}

这里下图更能直观解释每次重载会调用其对于类型的重载函数。

但是这种方法效率会降低,可以加上以下三行代码提高效率。

ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

流插入ostream函数:

如:

ostream& operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;return out;}

但是对应的会出现错误

void TestDate6()
{//cout<<d1此时会报错,因为左右参数配不上//d1 << cout;也会有一点小错误
}

参数配不上的图解:这里out就是cout的别名

所以上述ostream函数有错误,不能重载为成员函数,否则参数会对不上。

//cout<<d1参数对应上了
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

这里问题就是访问私有变量:

解决方法:(1)提供一个get成员函数  (2)声明为友元函数

......
//友元声明  可以放到任意位置,一般放到类开始
friend ostream& operator<<(ostream& out, const Date& d);
private:int _year;int _month;int _day;
};

但是此时友元为全局函数,会在多个文件中包含,符号表就会重复,需要声明和定义分离。

解决方法:(1)h中声明,cpp中定义  (2)在函数前加上inline

流提取函数:

istream& operator>>(istream& in, Date& d);

不加const,因为提取的值要放到该对象中,所以加引用。

友元声明:

friend istream& operator>>(istream& in, Date& d);
inline istream& operator>>(istream& in, Date& d)
{while (1){cout << "请依次输入年月日:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "输入日期非法,请重新输入!!!" << endl;}else{break;}}return in;
}void TestDate6()
{Date d1(2025, 3, 0);Date d2;cin >> d1 >> d2;//连续输入输出cout << d1 << d2;
}

2友元

(1)友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类 声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯

(2)外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数

(3)友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制

(4)⼀个函数可以是多个类的友元函数

void func(const A& aa, const B& bb)
{cout << aa._a1 << endl;cout << bb._b1 << endl;
}
//可以把这个函数既放到类域A中又放到类域B中

(5)友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。

// 前置声明,否则A的友元函数声明编译器不认识B 
class B;
class A
{// 友元声明 friend void func(const A& aa, const B& bb);
private:int _a1 = 1;int _a2 = 2;
};
class A
{// 友元声明 friend class B;
private:int _a1 = 1;int _a2 = 2;
};
class B
{
public:void func1(const A& aa){cout << aa._a1 << endl;cout << _b1 << endl;}void func2(const A& aa){cout << aa._a2 << endl;cout << _b2 << endl;}
private:int _b1 = 3;int _b2 = 4;
};

(6)友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。

(7)友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。

(8)有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。

七,内部类

(1)如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在 全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。

(2)内部类默认是外部类的友元类。

(3)内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了。

class A
{
private:static int _k;int _h = 1;
public:class B // B默认就是A的友元 {public:void foo(const A& a){cout << _k << endl; //OKcout << a._h << endl; //OK}int _b1;};
};int A::_k = 1;int main()
{cout << sizeof(A) << endl;//B:: _b1; 是访问不到的,受到A类域的限制A::B b;A aa;b.foo(aa);return 0;
}

题目1:

求1+2+3+...+n_牛客题霸_牛客网

class Solution 
{// 内部类 class Sum{public:Sum(){_ret += _i;++_i;}};static int _i;static int _ret;public:int Sum_Solution(int n){// 变⻓数组 Sum arr[n];return _ret;}
};
int Solution::_i = 1;
int Solution::_ret = 0;

图解:

八,匿名对象

临时对象:(1)类型转换时会产生   (2)传值返回会产生

1,⽤类型(实参)定义出来的对象叫做匿名对象,相⽐之前我们定义的类型对象名(实参)定义出来的 叫有名对象。

2,匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

int main()
{// 有名对象A aa1(1);// 转换产生的临时对象  const引用可以延长其生命周期const A& aa2 = 1;// 但是他的⽣命周期只有这⼀⾏,我们可以看到下⼀⾏他就会⾃动调⽤析构函数 // 匿名对象A(2);A aa1;// 不能这么定义对象,因为编译器⽆法识别下⾯是⼀个函数声明,还是对象定义 //A aa1();// 但是我们可以这么定义匿名对象,匿名对象的特点不⽤取名字, return 0;
}
class Solution 
{public:int Sum_Solution(int n){//...return n;}
};
int main()
{//有名调用Solution s;s.Sum_Solution(10);//匿名调用Solution().Sum_Solution(10);
}

九,对象拷贝时的编译器优化

(1)现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传返 回值的过程中可以省略的拷⻉。

(2)如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编 译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译器还会进⾏ 跨⾏跨表达式的合并优化。

(3)linux下可以将下⾯代码拷⻉到test.cpp⽂件,编译时⽤ g++ test.cpp -fno-elide-constructors 的⽅式关闭构造相关的优化。

int main()
{// 构造临时对象,临时对象再拷贝构造aa1,优化为直接构造//A aa1 = 1;// 传值传参// 无优化A aa1;//f1(aa1); 已经创建出对象,再去拷贝构造是没有优化的//cout << endl;//下面两者情况是有优化的隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));//cout << endl;
}

图解观察:

传值返回:先构造,再创建临时对象,返回临时对象,再调用拷贝构造。

A f2()
{A aa;return aa;
}int main()
{// 传值返回// 返回时一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造 (vs2019 debug)// 一些编译器会优化得更厉害,进行跨行合并优化,直接变为构造。(vs2022 debug)f2();cout << endl;// 返回时一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造 (vs2019 debug)// 一些编译器会优化得更厉害,进行跨行合并优化,直接变为构造。(vs2022 debug)A aa2 = f2();cout << endl;// 接收返回值时A ret=f2();cout<<endl;
}

未接收返回值时图解:左侧关闭优化,右侧开启

接收返回值时图解:构造+拷贝构造+析构,返回值给ret时又要拷贝构造+析构。

拷贝构造+赋值重载:

未优化:构造+拷贝构造+析构+赋值重载+析构

int main()
{// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}

图解:

总结:

相关文章:

C++:类和对象4

一&#xff0c;日期类实现 学习建议&#xff1a; 对于计算机学习来说&#xff0c;调试十分重要&#xff0c;所以在日常学习中一定要加大代码练习&#xff0c;刷代码题和课后自己敲出课上代码例题&#xff0c;注意不要去对比正确代码或者网上找正确代码直接使用&#xff0c;一…...

【软件工程】基于机器学习的多缺陷定位

基于机器学习的多缺陷定位&#xff08;Multi-Dault Localization, MDL&#xff09;是软件工程和自动化测试领域的重要研究方向&#xff0c;旨在通过机器学习技术高效识别代码中多个潜在缺陷的位置。以下从方法、挑战、应用场景及未来方向展开分析&#xff1a; 一、核心方法 监督…...

互联网大厂Java求职面试实战:Spring Boot到微服务的技术问答解析

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…...

LLMs之MCP:2025年5月2日,Anthropic 宣布 Claude 重大更新:集成功能上线,研究能力大幅提升

LLMs之MCP&#xff1a;2025年5月2日&#xff0c;Anthropic 宣布 Claude 重大更新&#xff1a;集成功能上线&#xff0c;研究能力大幅提升 导读&#xff1a;2025年5月2日&#xff0c;Anthropic 宣布 Claude 推出 Integrations 集成功能和增强型高级研究功能。Integrations 基于 …...

飞蛾扑火算法matlab实现

注意&#xff1a;此代码实现的是求目标函数最大值&#xff0c;求最小值可将适应度函数乘以-1&#xff08;框架代码已实现&#xff09;。 注意&#xff1a;此代码实现的是求目标函数最大值&#xff0c;求最小值可将适应度函数乘以-1&#xff08;框架代码已实现&#xff09;。 注…...

vector--OJ1

链接: link class Solution { public:int singleNumber(vector<int>& nums) {int ret0;for(auto a : nums){ret^a;}return ret;} };链接: link class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>>…...

多模态大语言模型arxiv论文略读(六十八)

Image-of-Thought Prompting for Visual Reasoning Refinement in Multimodal Large Language Models ➡️ 论文标题&#xff1a;Image-of-Thought Prompting for Visual Reasoning Refinement in Multimodal Large Language Models ➡️ 论文作者&#xff1a;Qiji Zhou, Ruoc…...

【数据库知识】Mysql进阶-高可用MHA(Master High Availability)方案

mysql高可用MHA&#xff08;Master High Availability&#xff09;方案 集群部署模式下的高可用方案一、高可用架构原理1. 核心组件2. 故障切换流程 二、详细部署步骤 (3节点集群)1. 环境准备2. 节点配置&#xff08;以 node1 为例&#xff09;3. 初始化集群4. 部署MySQL Route…...

类型别名与接口的对比与选择

在 TypeScript 中&#xff0c;类型系统是非常强大且灵活的。两种最常用的类型定义方式就是 类型别名&#xff08;type&#xff09; 和 接口&#xff08;interface&#xff09;。它们看似相似&#xff0c;实际上在用法和功能上有所不同。在本文中&#xff0c;我们将深入探讨类型…...

《Effective Python》第1章 Pythonic 思维详解——始终用括号包裹单元素元组

《Effective Python》第1章 Pythonic 思维详解——始终用括号包裹单元素元组 在 Python 编程语言中&#xff0c;元组&#xff08;tuple&#xff09;是一种不可变的数据结构&#xff0c;常用于表示一组固定的值。尽管元组的语法看似简单&#xff0c;但其中却隐藏着一些微妙的陷…...

【计算机视觉】OpenCV实战项目:ETcTI_smart_parking智能停车系统深度解析

ETcTI_smart_parking智能停车系统深度解析 1. 项目概述2. 技术原理与系统架构2.1 核心算法1) 车牌识别算法2) ETC交易验证 2.2 系统架构 3. 实战部署指南3.1 环境配置3.2 硬件部署规范3.3 系统初始化 4. 常见问题与解决方案4.1 ETC交易失败4.2 车牌识别异常4.3 系统性能瓶颈 5.…...

LintCode第807题-回文数II

描述 判断一个非负整数 n 的二进制表示是否为回文数 我们保证 0 < n < 2^32 - 1 样例1 输入: n 0 输出: True 解释: 0 的二进制表示为&#xff1a;0。 样例2 输入: n 3 输出: True 解释: 3 的二进制表示为&#xff1a;11。 样例3 输入: n 4 输出: False 解释:…...

快速傅里叶变换暴力涨点!基于时频特征融合的高创新时间序列分类模型

往期精彩内容&#xff1a; 单步预测-风速预测模型代码全家桶-CSDN博客 半天入门&#xff01;锂电池剩余寿命预测&#xff08;Python&#xff09;-CSDN博客 超强预测模型&#xff1a;二次分解-组合预测-CSDN博客 VMD CEEMDAN 二次分解&#xff0c;BiLSTM-Attention预测模型…...

股指期货的保证金交易和资金门槛是多少?

股指期货和股票交易最大的区别&#xff0c;就是它不用“全款买房”&#xff0c;而是“首付买房”——只需交一笔保证金就能撬动大额资金&#xff0c;但这也说明了门槛高、风险大。下面就来拆解&#xff0c;到底要准备多少钱才能“上车”。 一、保证金交易&#xff1a;用12万撬…...

spark:map 和 flatMap 的区别(Scala)

场景设定 假设有一个包含句子的 RDD&#xff1a; scala val rdd sc.parallelize(List("Hello World", "Hi Spark")) 目标是&#xff1a;将每个句子拆分成单词。 1. 用 map 的效果 代码示例 scala val resultMap rdd.map(sentence > sentence…...

判断两台设备是否在同一局域网内的具体方法

以下是判断两台设备是否在同一局域网内的具体方法&#xff1a; 1. 检查IP地址和子网掩码 操作步骤&#xff1a; Windows系统&#xff1a; 按 Win R 键&#xff0c;输入 cmd 并回车。输入 ipconfig&#xff0c;查看 IPv4 地址 和 子网掩码&#xff08;如 192.168.1.5/255.255.2…...

cmake:test project

本文主要探讨cmake在测试和项目中的应用。 add_test add_test(NAME <name> COMMAND <command> [<arg>...] [CONFIGURATIONS <config>...] [WORKING_DIRECTORY <dir>] [COMMAND_EXPAND_LISTS])  add_test(NAME test_uni COMMAND $<TARGET_F…...

Qwen-2.5 omni

问题1 Qwen2.5-Omni的主要功能是什么&#xff1f; Qwen2.5-Omni的主要功能是处理和生成多种模态的数据&#xff0c;包括文本、图像、音频和视频。它能够同时理解和生成这些模态的信息&#xff0c;支持复杂的多模态任务&#xff0c;例如语音对话、视频理解、图像描述生成等。 …...

Problem D: 异常2

1.题目描述 检查危险品程序&#xff1a;生成货物类、机器类&#xff0c;货物类有属性name&#xff0c;机器类有检测的check方法&#xff0c;若货物名称是炸弹、毒药、刀具&#xff0c;则抛出异常提示。 代码如下&#xff1a; import java.util.Scanner; class goods{ Stri…...

AI智慧公园管理方案:用科技重塑市民的“夜游体验”

AI智慧公园管理方案&#xff1a;多场景智能巡检与安全防控 一、背景与痛点分析 夏季夜间&#xff0c;公园成为市民休闲娱乐的核心场所&#xff0c;但管理难度随之激增&#xff1a; 宠物管理失控&#xff1a;未牵绳宠物进入园区&#xff0c;随地排泄、惊扰游客&#xff0c;甚…...

AJAX原理

AJAX使用XHR 对象和服务器进行数据交互 XHR <p class"my-p"></p><script>const xhr new XMLHttpRequest()xhr.open(GET,http://hmajax.itheima.net/api/province)xhr.addEventListener(loadend,()>{// console.log(xhr.response)const data …...

内存泄露,如何判断是资源泄露还是堆栈泄露?

作为软件测试工程师,判断内存泄露类型对于定位和解决问题至关重要。以下是区分资源泄露和堆栈泄露的方法: 1. 基本概念区分 资源泄露(Resource Leak): 指非内存资源未正确释放,如文件句柄、数据库连接、网络套接字等 虽然不直接表现为Java堆内存增长,但会导致系统资源耗…...

无人甘蔗小车履带式底盘行走系统的研究

1.1 研究背景与意义 1.1.1 研究背景 甘蔗作为全球最重要的糖料作物之一&#xff0c;在农业经济领域占据着举足轻重的地位。我国是甘蔗的主要种植国家&#xff0c;尤其是广西、广东、云南等地&#xff0c;甘蔗种植面积广泛&#xff0c;是当地农业经济的重要支柱产业。甘蔗不仅…...

Redis设计与实现——单机Redis实现

RedisDB RedisDB的核心结构 键空间&#xff08;dict*dict&#xff09; 结构&#xff1a;哈希表&#xff08;字典&#xff09;&#xff0c;键为字符串对象&#xff08;SDS&#xff09;&#xff0c;值为 Redis 对象&#xff08;字符串、列表、哈希等&#xff09;。 功能&#x…...

ES C++客户端安装及使用

介绍 Elasticsearch &#xff0c; 简称 ES &#xff0c;它是个开源分布式搜索引擎&#xff0c;它的特点有&#xff1a;分布式&#xff0c;零配置&#xff0c;自动发现&#xff0c;索引自动分片&#xff0c;索引副本机制&#xff0c;restful 风格接口&#xff0c;多数据源&…...

C++学习之STL学习

在经过前面的简单的C入门语法的学习后&#xff0c;我们开始接触C最重要的组成部分之一&#xff1a;STL 目录 STL的介绍 什么是STL STL的历史 UTF-8编码原理&#xff08;了解&#xff09; UTF-8编码原理 核心编码规则 规则解析 编码步骤示例 1. 确定码点范围 2. 转换为…...

【东枫科技】使用LabVIEW进行NVIDIA CUDA GPU 开发

文章目录 工具包 CuLab - LabVIEW 的 GPU 工具包特性和功能功能亮点类似 LabVIEW 的 GPU 代码开发支持的功能数值类型和维数开发系统要求授权售价 工具包 CuLab - LabVIEW 的 GPU 工具包 CuLab 是一款非常直观易用的 LabVIEW 工具包&#xff0c;旨在加速 Nvidia GPU 上的计算密…...

LangChain对话链:打造智能多轮对话机器人

LangChain对话链:打造智能多轮对话机器人 目录 LangChain对话链:打造智能多轮对话机器人ConversationChain 是什么核心功能与特点基本用法示例内存机制自定义提示词应用场景与其他链的结合`SequentialChain` 是什么![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0…...

MIT 6.S081 2020 Lab3 page tables 个人全流程

文章目录 零、写在前面1、关于页表2、RISC-V Rv39页表机制3、虚拟地址设计4、页表项设计5、访存流程6、xv6 的页表切换7、页表遍历 一、Print a page table1.1 说明1.2 实现 二、A kernel page table per process2.1 说明2.2 初始化 / 映射相关2.3 用户内核页表的创建和回收2.4…...

spring cloud loadbalancer实现机房感知的负载均衡

1 概述 在同城多机房情景下&#xff0c;各个机房各自部署一套微服务集群&#xff0c;正常情况下微服务调用在本机房闭环。在如下某些灾难情景&#xff0c;可以尝试拉远调用以最大程度维持业务连续性&#xff0c;这些情景例如&#xff1a; A机房多个服务器宕机。应用由于BUG发…...

vs2022配置opencv

一、下载opencv 1、进入https://opencv.org/官网下载 2、打开下载的exe开始安装&#xff0c;自己新建一个文件夹opencv作为安装路径 3、安装完成得到opencv文件内容 4、开始配置环境变量 4.1、复制下图的路径 4.2、添加到系统环境变量 5、配置VS项目环境 5.1、添加包含目录 …...

Appium-OppoA92S-真机记坑

问题现象 使用Appium操作真机时候&#xff0c;需要安装AppiumSettings软件&#xff0c;但是实际操作&#xff0c;一直提示安装失败&#xff0c;安装包异常 问题分析 [待定] 网上找到方式&#xff0c;都尝试过&#xff0c;无效 问题解决 取消开发者模式&#xff0c;USB连接手…...

ENSP-OSPF综合实验

AR4中通过ospf获取的其他区域路由信息&#xff0c;并且通过路由汇总后简化路由信息 实现全网通&#xff0c;以及单向重发布&#xff0c;以及通过缺省双向访问&#xff0c; 通过stub简化过滤四类五类lsa&#xff0c;简化ospf路由信息 通过nssa简化ospf信息 区域汇总简化R4路由信…...

基于VSCode+PlatformIO环境的ESP8266的HX1838红外模块

以下是针对ESP8266开发板的红外遥控解码系统开发教程&#xff0c;基于VSCodePlatformIO环境编写 一、概述 本实验通过ESP8266开发板实现&#xff1a; 红外遥控信号解码自定义按键功能映射串口监控输出基础设备控制&#xff08;LED&#xff09; 硬件组成&#xff1a; NodeMC…...

HTML12:文本框和单选框

表单元素格式 属性说明type指定元素的类型。text、password、 checkbox、 radio、submit、reset、file、hidden、image 和button&#xff0c;默认为textname指定表单元素的名称value元素的初始值。type为radio时必须指定一个值size指定表单元素的初始宽度。当type为text 或pas…...

JVM规范之运行时数据区域

JVM运行时数据区 前言为什么要阅读jvm规范&#xff1f;阅读本篇文章可以学习到啥&#xff1f; 正文概述JVM线程私有的运行时数据区pc(program counter) RegisterJVM StackNative Method Stack JVM线程共享的运行时数据区HeapMethod AreaRun-time constant pool 总结参考链接 前…...

LVGL(lv_btnmatrix矩阵按钮)

文章目录 &#x1f527; 1. 基本概念&#x1f4cc; lv\_btnmatrix 是什么&#xff1f; &#x1f9f1; 2. 基本结构和用法✅ 创建按钮矩阵✅ 设置按钮文字 &#x1f9f0; 3. 设置按钮行为&#x1f504; 4. 响应按钮点击&#x1f3a8; 5. 自定义样式&#x1f4cc; 6. 使用技巧&am…...

AUTOSAR图解==>AUTOSAR_TR_AIDesignPatternsCatalogue

AUTOSAR 人工智能设计模式目录 AUTOSAR传感器执行器与仲裁设计模式的深入解析与图解 目录 简介传感器和执行器模式 架构概述组件结构交互流程应用场景 多请求者或提供者之间的仲裁模式 架构概述组件结构仲裁流程应用场景 总结 1. 简介 AUTOSAR&#xff08;AUTomotive Open Sy…...

英语时态--中英文对“时间”的不同理解

文章目录 中英文时间上的差异我现在正在休息一般现在时1. 经常发生的动作2. 表客观事实3. 表示现在的状态一般将来时1. will2. be going to含义1:打算在将来某时做某事含义2:某事预计要发生而且不可避免中英文时间上的差异 我现在正在休息 “我现在正在休息。”用英文怎么说…...

Linux基础开发工具一(yum/apt ,vim)

前言 Linux下&#xff0c;如何进行软件安装&#xff0c;查找&#xff0c;卸载 1.源代码安装 2. rpm安装方式&#xff0c;安装包的本质&#xff0c;就是把源代码在Linux下编译好&#xff0c;然后打包&#xff08;别人把自己编译好的软件打包给你让你去安装&#xff09; 上面…...

Java 线程池原理

Java 线程池是一种管理和复用线程的机制&#xff0c;其原理如下&#xff1a; 核心概念 线程池的初始化 &#xff1a;在创建线程池时&#xff0c;需要设置一些关键参数&#xff0c;如核心线程数&#xff08;corePoolSize&#xff09;、最大线程数&#xff08;maximumPoolSize&am…...

AJAX 使用 和 HTTP

ajax学习 promise和 awit Node.js 和 webpack 前端工程化 Git工具 AJAX异步的JS和XML&#xff1a; 使用XML对象和服务器通信 在这里插入图片描述 统一资源定位符 URL HTTP 超文本传输协议 域名 资源路径 资源目录和类型 URL 查询参数 使用&#xff1f;表示之后的参数…...

mem0跟Memgraph交互

目录 1. 安装和设置2. 配置连接3. 使用 mem0 进行交互4. 添加和查询数据5. 代码运行结果 1. 安装和设置 首先&#xff0c;确保你已经安装了 Memgraph 和 mem0 库。你可以使用 pip 来安装 mem0&#xff1a; uv pip install "mem0ai[graph]" uv pip install langchai…...

httpclient请求出现403

问题 httpclient请求对方服务器报403&#xff0c;用postman是可以的 解决方案: request.setHeader( “User-Agent” ,“Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0” ); // 设置请求头 原因&#xff1a; 因为没有设置为浏览器形式&#…...

验证码与登录过程逻辑学习总结

目录 前言 一、验证码与登录 二、使用步骤 1.先apipost测试一波 2.先搞验证码 3.跨域问题 4.后端走起 总结 前言 近期要做一个比较完整的demo&#xff0c;需要自己做一个前端登录页面&#xff0c;不过api接口都是现成的&#xff0c;一开始以为过程会很easy&#xff0c;…...

openai接口参数max_tokens改名max-completion-tokens?

文章目录 关于max_tokens参数max_tokens改max-completion-tokens&#xff1f;控制 OpenAI 模型响应的长度 关于max_tokens参数 大模型 API&#xff08;比如 OpenAI、DeepSeek、Moonshot 等&#xff09;都是按照“Token 数量”来计费。 控制内容&#xff1a;控制一次请求返回的…...

【办公类-39-07】20250511批量生成通义万相图片(七)彩色背景蝴蝶-筛选无黑无白的图片

制作了教师裁剪的拼图块后&#xff0c;发现如果图案上有黑色&#xff0c;就没法看清出凹凸角&#xff0c;剪起来有误差 所以黑色也要剔除 通义万相下载的彩色蝴蝶有彩色背景的图片&#xff0c;放弃有白色和黑色图案的图片&#xff0c;保留彩色图案的蝴蝶 deepseek、阿夏 20250…...

容器技术 20 年:颠覆、重构与重塑软件世界的力量

目录 容器技术发展史 虚拟化技术向容器技术转变 Docker的横空出世 容器编排技术与Kubernetes 微服务的出现与Istio 工业标准的容器运行时 容器技术与 DevOps 的深度融合​ 无服务架构推波助澜 展望未来发展方向 从 20 世纪硬件虚拟化的笨重&#xff0c;到操作系统虚拟…...

Docker 部署 - Crawl4AI 文档 (v0.5.x)

Docker 部署 - Crawl4AI 文档 (v0.5.x) 快速入门 &#x1f680; 拉取并运行基础版本&#xff1a; # 不带安全性的基本运行 docker pull unclecode/crawl4ai:basic docker run -p 11235:11235 unclecode/crawl4ai:basic# 带有 API 安全性启用的运行 docker run -p 11235:1123…...

在C++中,符号位是否参与位运算

在C中&#xff0c;符号位是否参与位运算取决于具体的运算符和数据类型。以下是详细解释&#xff1a; 1. 按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09; 规则&#xff1a;这些运算符会处理包括符号位在内的所有二…...