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

C++ 类和对象

面向过程/面向对象

C语言是面向过程,关注过程,分析出求解问题的步骤,通过函数调用逐步解决问题

C++是基于面对对象的,关注的是对象——将一件事拆分成不同的对象,依靠对象之间的交互完成

引入

C语言中结构体只能定义变量,但是在C++中,结构体不仅可以定义变量,也能定义函数

以栈为例,C++里对于栈的函数可以这样子写:

//C++
struct Stack
{//成员变量int* a;int top;int capacity;//成员函数——直接将函数与成员变量放在一起定义void Init(){a = nullptr;top = capacity = 0;}void Push(int x){}
};//C
void StackInit(struct Stack* ps)
{}
void StackPush(struct Stack* ps,int x)
{}

类的定义

class className
{//类体:由成员函数和成员变量组成};//有分号

类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者

成员函数。

两种定义方式

1.声明和定义全部放在类体中

class Date
{
public:void Init(int year, int month, int day){//域搜索,一个是类域的year,一个是函数局部域的year//所以需要加上_来区分_year = year;_month = month;_day = day;}
private:int _year;//属于声明int _month;int _day;
};

2.类声明放在.h中,成员函数定义在.cpp文件中,且成员函数名前需要加类名

//.h
class Stack
{
public://成员函数void Init();void Push(int x);int Top();
private://访问限定符,访问到下一个访问限定符或者到函数末尾才会结束//这样在主函数里就无法访问int* a;int top;int capacity;
};//.cpp
void Stack::Init()//全局函数,需要在其他文件里去找,而不是到类域里去找
{a = nullptr;top = capacity = 0;
}
void Stack::Push(int x)//需要有访问符
{if (top == capacity){size_t newcapacity = capacity == 0 ? 4 : capacity * 2;a = (int*)realloc(a, sizeof(int) * newcapacity);capacity = newcapacity;}a[top++] = x;
}
int Stack::Top()
{return a[top - 1];
}

访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选 择性的将其接口提供给外部的用户使用。

说明

1. public修饰的成员在类外可以直接被访问

2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的) 3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止

4. 如果后面没有访问限定符,作用域就到 } 即类结束。

5. class的默认访问权限为private,struct为public(因为struct要兼容C)

访问

以Date类为例

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year<<" ";cout << _month << " ";cout << _day << " ";cout << endl;}
//private:int _year;//因为此处是声明,不是定义//声明特点:只有一个躯体,不能访问,没有分配空间int _month;int _day;
};

1.由于private中对于成员变量,是声明而不是定义——故不能直接访问,需要先建立个对象

int  main()
{Date d;d._year=1//errreturn 0;    
}

无法访问——因为类中_year被private访问限定符限制住了,所以不能在类外访问

如何解决——将' private '注释掉

2.类外调用函数

若是想调用函数,首先就需要定义一个对象d——然后在用d.函数名()去访问

例如此处的打印函数,想打印类的内容

int main()
{Date d;d1.Init(2022,2,22);//先初始化再打印d1.Print();return 0;
}

类的大小(存储方式)

同理,我们对于日期类Date进行讨论

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year<<" ";cout << _month << " ";cout << _day << " ";cout << endl;}
private:int _year;int _month;int _day;
};

进行大小的打印

int main()
{Date d1;cout<<sizeof(d1)<<endl;cout<<sizeof(Date)<<endl;return 0;
}

类的大小仅取决于成员变量,成员函数存放在公共代码区中,不会计入类的大小,仅对象的成员变量会算入大小

 若成员变量不为同类型呢?

class A4
{
public:void f1() {}private:char _c;int _a;
};

为8——同理按照编译器内存对齐的规律来进行的 

类的实例化

用类类型创建对象的过程,称为类的实例化

1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;

2. 一个类可以实例化出多个对象,实例化出的对象才占用实际的物理空间,存储类成员变量

int main()
{Date._year=1;//errreturn 0;
}

this指针

现在对于一个日期的类Date

class Date
{
public:void Init(int year,int month,int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;//声明,不是定义int _month;int _day;
};int main()
{Date d1;d1.Init(2022,3,15);Date d2;d2.Init(2023,3,15);return 0;
}

对于Init 和 Print 函数,看起来分别有3个和0个参数,但事实并非如此——分别有4个和1个,为什么?

这两个函数内部还有一个隐含参数——this指针,会将函数进行一个修改:

 同理,对于主函数里的初始化函数:

 建立联系

 所以函数可以写成

void Init(int year,int month,int day){this->_year = year;this->_month = month;this->_day = day;}void Print(){cout << this->_year << "/" << this->_month << "/" << this->_day << endl;}

注意!!!!

this指针在实参和形参位置不能写,但是在类里可以直接写出来使用(但没必要)-指向当前对象

若在函数里嵌套另一个成员函数呢?指向两个Print函数的this指针是否一样?

void Init(int year,int month,int day){this->_year = year;this->_month = month;this->_day = day;Print();}void Print(){cout << this->_year << "/" << this->_month << "/" << this->_day << endl;}

不一样——一个对象中,两个this指针是各自的不同的指针变量,但是指向的地方是一样的。

若是两个不同的对象,那么这两个this指针不仅不同,指向的空间也不同

特性

1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。

2. 只能在“成员函数”的内部使用

3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给

this形参。所以对象中不存储this指针。

4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递

两个例题

1

有以下代码

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}

由于定义了一个对象p,但是为空指针,要去调用成员函数——需要在对象里找吗?

不,成员函数定义在公共函数区域里,所以可以直接调用

2

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:void PrintA() {cout<<_a<<endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}

此处运行崩溃——因为p是空指针,且要访问_a,需要用到this指针去访问this->_a

但此处this指针为空,所以会导致运行崩溃

综上,类的访问都是先传递this指针,再去找到函数的地址并且使用函数

this指针指向的是需要作用的对象

类的6个默认成员函数

若有

class Date{};

那么这个类成为空类——但是这个类里什么都没有吗????

实际上,编译器会自动生成以下6个默认成员函数

构造函数

构造函数负责初始化,函数名就是类名

特征

1. 函数名与类名相同。

2. 无返回值。

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

4. 构造函数可以重载。

class Date
{
public://正常初始化void Init(int year, int month, int day){_year = year;_month = month;_day = day;}//构造函数Date()//初始化{_year=1;_month=1;_day=1;}Date(int year, int month, int day)//直接输入,用缺省参数{_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;   
};

为了方便,我们可以直接将两个构造函数合并

Date(int year=1, int month=1, int day=1)//直接输入,用全缺省参数,减少了代码量{_year = year;_month = month;_day = day;}

且考虑到其他函数内部会没有定义的值,所以可以在声明成员变量的时候,给予其缺省值

故类可以修改为

class Date
{
public:Date(int year=1, int month=1, int day=1)//直接输入,用全缺省参数,减少了代码量{cout << "Date(int year=1, int month=1, int day=1)" << endl;_year = year;_month = month;_day = day;}void Print()//实际上为1个参数{cout << _year << "/" << _month << "/" << _day << endl;//cout << this << endl;}
private:int _year;int _month;int _day;
};

如果用户显式定义了构造函数,那么编译器就不会生成默认的构造函数

构造函数的出现,减少了代码量

int main()
{   //非构造Date d1;d1.Init(2022,2,22);Date d2(2022,2,22);return 0;
}

一行代码就能实现对象的创建和初始化

注意

不实现构造函数的情况下,编译器会生成默认的构造函数,虽然日期类Date对象d调用了默认构造函数但是其成员变量_year/_month/_day仍然为随机值,这是为什么?

——C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类 型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型。

因此,为了解决对于内置类型成员不初始化的缺陷,我们可以在成员变量声明时给予默认值

private:int _year=1;int _month=1;int _day=1;

析构函数

与构造函数相反,析构函数是用于对于对象中资源的清理

特征

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

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

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

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

同理以Date为例

class Date
{
public:Date(int year=1, int month=1, int day=1)//直接输入,用全缺省参数,减少了代码量{cout << "Date(int year=1, int month=1, int day=1)" << endl;_year = year;_month = month;_day = day;}void Print()//实际上为1个参数{cout << _year << "/" << _month << "/" << _day << endl;//cout << this << endl;}~Date(){cout << "~Date()" << endl;}
private:int _year=1;//声明,默认会用给的缺省值进行初始化,内置类型不作处理int _month=1;//作为缺省值,会补充其他函数里没有定义的值int _day=1;
};

若不写析构函数,那么编译器就会默认生成析构函数来进行资源的清理

以栈来展现构造和析构函数

class Stack
{
public:/*Stack(){a = nullptr;top = capacity = 0;}*/Stack(size_t n = 4)//不传入值则就默认4个空间{cout << "Stack(size_t n = 4)" << endl;if (n == 0){a = nullptr;top = capacity = 0;}else{a = (int*)malloc(sizeof(int) * n);if (a == nullptr){perror("realloc fail");exit(-1);}top = 0;capacity = n;}}~Stack(){cout << "~Stack()" << endl;free(a);a = nullptr;top = capacity = 0;}void Push(int x){if (top == capacity){size_t newcapacity = capacity == 0 ? 4 : capacity * 2;int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);if (tmp == nullptr){perror("realloc fail");exit(-1);}if (tmp = a){cout << capacity << "原地扩容" << endl;//原地扩容还是异地扩容呢?//若打印就原地,否则就异地}else{cout << capacity << "异地扩容" << endl;}a = tmp;capacity = newcapacity;}a[top++] = x;}int Top(){return a[top - 1];} void Pop(){assert(top > 0);--top;}void Destroy(){free(a);a = nullptr;top = capacity = 0;}bool Empty(){if (top == 0)return 1;return 0;}
private:int* a;int top;int capacity;
};

在构造函数处,我们选择用了缺省参数,这样可以随机初始化自己想要的栈的大小

int main()
{Stack st1;Stack st2(10);return 0;
}

 那么,对于类中这种定义的函数,我们可以这样子调用

int main()
{    Stack st2(10);st2.Push(1);st2.Push(2);st2.Push(3);st2.Push(4);st2.Push(5);st2.Push(6);st2.Push(7);st2.Push(8);st2.Push(9);st2.Push(10);while (!st2.Empty()){cout << st2.Top() << " ";st2.Pop();}cout << endl;return 0;
}

也可以直接传入1000的量,这样子无需要扩容

    Stack st2(1000);    for (size_t i = 0; i < 1000; i++){st2.push(i);}

注意:

1.会自动构造 
2.后定义,先析构,若没有写析构函数,那么编译器就会默认生成一个析构函数
3.内置类型成员不处理,但是会自定义类型成员会调用在这个成员的析构函数

但是栈里一定要写析构函数!!!——析构函数是浅处理,就是处理像int /char 这种普通类型的变量,随着函数的结束就会销毁,但是栈里有定义——int* a,也就是数组,需要手动处理,不然会导致内存泄漏的问题!!!!

特殊

class MyQueue
{
private:Stack _pushst;//初始化的时候已经生成Stack _popst;};

对于此类,尽管有int* a指针定义,也需要写——因为定义Stack的时候就已经生成了相关的构造和析构函数,此时就不需要我们去写

除此之外,构造函数和析构函数的调用就类似于栈,先构造的最后析构

拷贝构造函数

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

特征:

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

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

无穷递归错误情况代码:

class Date
{
public:Date(Date d)   // 错误写法:编译报错,会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};

 同理,在自定义函数里,也不能直接传值传入

void func2(Stack s)
{s.Push(1);s.Push(2);
}

这里直接传参的话,那么就是用形参s接收一个对象,此时对象和形参都指向一个空间,那么函数结束后形参指向的空间会进行销毁,那么原本对象指向的空间将会不存在,会导致报错(析构两次)

那怎么解决?——用引用,获取对象的地址 ,改为如下:

class Date
{
public:Date(Date& d)   // 错误写法:编译报错,会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};void func2(Stack& s)
{s.Push(1);s.Push(2);
}

如何定义?

——有' () '  也有 ' = ' 两种形式

int main()
{Date d1(2022,2,22);Date d2(d1);Date d3=d1;d1.Print();d2.Print();d3.Print();return 0;
}

先构造d1,然后直接将d1拷贝构造给d2和d3,最后再逐一析构

若不写拷贝构造函数呢?

—— 编译器会自己生成

class Date
{
public://Date(Date& d) //{// _year = d._year;//_month = d._month;//_day = d._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2022,2,22);Date d2=d1;//passreturn 0;
}

 但是对于栈,若不写拷贝构造函数——则会报错err

Stack s1;
Stack s2=s1;

 所以对于栈一定要进行自定义拷贝函数

Stack(const Stack& s)//用const防止代码赋值写反导致改变了原对象的内容{a = (int*)malloc(sizeof(int) * s.capacity);if (a == NULL){perror("malloc申请失败");return;}memcpy(a, s.a, sizeof(int) * s.top);capacity = s.capacity;top = s.top;}

总结:

1.内置类型,值拷贝
2.自定义类型,调用他的拷贝
总结:日期类Date 不需要我们自己实现拷贝构造,默认生成就可以用
          栈类Stack 需要我们自己实现深拷贝的拷贝构造,默认生成会出现问题
          因为Stack需要释放空间,防止栈溢出内存泄漏,但是Date里的变量会随着空间的销毁而销毁。

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数原型:返回值类型 operator操作符(参数列表)

再次以日期类Date为例

实现日期类的比较——两个不同日期的大小区分

全局

bool operator<(const Date& d1, const Date& d2)//用一个函数去比较,有别名接收
{//1.访问对象,将private注释掉if (d1._year < d2._year){return true;}else if (d1._year == d2._year && d1._month < d2._month){return true;}else if (d1._year == d2._year && d1._month == d2._month && d1._day < d2._day){return true;}elsereturn false;
}

此处需要用到拷贝构造函数,用两个形参承载,并且进行比较(此时不能用private进行限定)

如果写入类里面呢?

注意

1.不能通过连接其他符号来创建新的操作符:比如operator@

2.重载操作符必须有一个类类型参数

3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this

5.    .      *   ::     sizeof     ?:    .这五个运算符不能重载

类内部定义运算符重载函数

bool operator<(const Date& d)//用一个函数去比较,用别名接收,参数个数为运算数-1{//1.访问对象,将private注释掉if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}elsereturn false;}

在主函数里调用

int main()
{//自定义类型怎么比较大小?——本身不支持比较,需要通过运算符重载才可以进行比较Date d1(2023, 7, 21);Date d2(2022, 8, 21);cout << (d1 < d2) << endl;cout << (operator<(d1, d2)) << endl;cout << (d1.operator<(d2)) << endl;return 0;
}

需要注意的是,左操作数是this,指向调用函数的对象

bool operator<(Date* this,const Date& d)

实现日期是否相同

bool operator<(const Date& d)//用一个函数去比较,用别名接收{//1.访问对象,将private注释掉if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}elsereturn false;}
bool operator==(const Date& d)
{return _year == d._year && _month == d._month && _day == d._day;
}

this指针的复用!!!

对于判断日期是否大于,可以通过对两个函数的复用来实现

bool operator<=(const Date& d)//此时d1作为operator,this指向d1,然后d2作为d{return *this < d || *this == d;//直接用this指针实现复用其它函数
}bool operator>=(const Date& d)
{return !(*this == d);
}

日期天数的改变

因位需要改变天数,也就是在原对象上进行数据修改,所以要拷贝构造

int GetMonthDay(int year, int month)
{int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((month==2)&&(year % 4 == 0 && year % 100 != 0)||(year%400==0)){return 29;}return monthArray[month];
}Date& operator+=(int day)//传值返回需要用拷贝构造,所以用引用返回
{_day += day;while (_day>GetMonthDay(_day,_month)){_day -= GetMonthDay(_day, _month);++_month;if (_month == 13){_year++;_month = 1;}}return *this;//此时要返回一个日期,日期存在于this指针里,所以要返回*this
}//出了作用域this指针仍存在,所以可以使用引用符号

日期天数的增加

由于仅进行天数的增加然后拷贝给别人,所以需要先拷贝构造一个载体tmp,然后对tmp进行修改,最后返回载体,这样子原对象的内容就得到了保证

Date operator+(int day)
{Date tmp(*this);//进行对this指针的拷贝构造,需要一个载体tmp += day; //this指向的就是d1里的数据,此时tmp就是*this,可以实现复用//用+=的运算符重载函数return tmp;
}
Date ret1 = d1 += 50;
ret1.Print();
d1.Print();//由于是+=,所以d1也会被改变Date ret=d1+50;//赋值一个日期,实际上也就是一个拷贝构造
ret.Print();
d1.Print();

实现类中成员函数的声明与定义分离

以日期类Date为例子

ps:若实现分离,那么缺省值由声明给,定义不能有缺省值

//Date.h
class Date
{
public:Date(int year = 1, int month = 1, int day = 1);//必须有缺省参数//为什么定义和声明不能分开?——因为要先找到声明再找到定义,类中会出现重定义//若想实现分开,那么就让声明给出缺省值,定义不用给void Print();int GetMonthDay(int year, int month);//连续赋值—返回指针Date& operator=(const Date& d);//实际上有两个参数,第一个参数就是this* d1,表示作用的对象,但是被隐藏了//只读函数可以加const,内部不涉及修改生成—是否const,都可以调用//若调用了,定义里也要加上constbool operator<(const Date& d);bool operator==(const Date& d);bool operator<=(const Date& d);bool operator>=(const Date& d);bool operator!=(const Date& d);Date& operator+=(int day);//用引用返回Date operator+(int day);
private:int _year;int _month;int _day;
};//Date.cpp  定义里需要用到访问符' :: '
int Date::GetMonthDay(int year, int month)//如果用这个函数时会改变其内容,那么就需要用const修饰
{const static int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((month == 2) && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){return 29;}return monthArray[month];
}//Date::Date(int year = 1, int month = 1, int day = 1)//err
Date::Date(int year, int month, int day)//pass
{_year = year;_month = month;_day = day;if (month < 1 || month>12 || day<1 || day>GetMonthDay(year, month)){cout << "非法日期" << endl;return;}}
//连续赋值—返回指针
Date& Date::operator=(const Date& d)//实际上有两个参数,第一个参数就是this* d1,表示作用的对象,但是被隐藏了
{if (this != &d){this->_year = d._year;this->_month = d._month;this->_day = d._day;}return *this;//this这里指向的是d1,因为作用的对象就是d1,出了作用域this还在,所以能用引用返回//自定义类型不能传值拷贝,需要调用一个拷贝构造
}bool Date::operator<(const Date& d) //用一个函数去比较,用别名接收
{//1.访问对象,将private注释掉if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}elsereturn false;
}
bool Date::operator==(const Date& d) 
{return _year == d._year && _month == d._month && _day == d._day;
}bool Date::operator<=(const Date& d) const //此时d1作为operator,this指向d1,然后d2作为d
//再通过d1<d2 和d1==d2的判断 来得出<=的结果
{return *this < d || *this == d;//直接用this指针实现复用其它函数
}bool Date::operator>=(const Date& d)  
{return !(*this == d);
}bool Date::operator!=(const Date& d) 
{return !(*this == d);
}Date& Date::operator+=(int day)
{if (day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_day, _month)){_day -= GetMonthDay(_day, _month);++_month;if (_month == 13){_year++;_month = 1;}}return *this;//此时要返回一个日期,日期存在于this指针里,所以要返回*this
}Date Date::operator+(int day) //若不想改变原内容,那么就需要一个变量来承载d1的this指针,然后改变变量,返回载体
{Date tmp(*this);//Date tmp = *this;tmp += day;return tmp;
}

赋值运算符重载

1.赋值运算符重载格式

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

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

检测是否自己给自己赋值

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

倘若在类中进行了拷贝不想原对象被改变,可以在定义的时候给予const进行修饰

Date (const Date& d);//拷贝构造
bool operator<(const Date& d);
bool operator<=(const Date& d);
bool operator>=(const Date& d);
bool operator>(const Date& d);

2.const重载

倘若定义对象的时候不想对象被改变,那么就会有以下的代码

void Print();void TestDate2()
{const Date d1(2023, 7, 28);//若在Print函数里就是权限的放大d1.Print();
}

但是此时Print()函数只能兼容普通类型,那怎么办?——仅需要加个关键字

void Print() const;//于此同时,定义也需要改变//void Date::Print(const Date* this)
void Date::Print() const//在结尾加上const,这样可以自由实现权限的缩小
{cout << _year <<"年" <<_month<<"月"<<_day<<"日"<< endl;
}

那么,为了代码的严谨并且保护原对象,那么对于只读函数,都可以加上const来修饰

    bool operator<(const Date& d)const;bool operator==(const Date& d)const;bool operator<=(const Date& d)const;bool operator>=(const Date& d)const;bool operator!=(const Date& d)const;

运算符赋值

我们知道有拷贝构造,那么对于运算符'=',应该也能实现两个对象之间的拷贝

void operator=(const Date& d)//实际上有两个参数,第一个参数就是this* d1,表示作用的对象,但是被隐藏了
{//赋值拷贝//此处用不用&? —都行,为什么不会无穷递归?——此处直接传入值,不会再进行多余的拷贝构造//为了方便,默认都用引用&this->_year = d._year;this->_month = d._month;this->_day = d._day;
}

但是若是想实现多次赋值呢?例如这样的:

int  main()
{int i,j,k;k=10;i=j=k;return 0;
}

那么我们需要一个返回值,也就是需要返回一个拷贝,才能实现多次赋值

Date& operator=(const Date& d)//实际上有两个参数,第一个参数就是this* d1,表示作用的对象,但是被隐藏了
{if (this != &d){this->_year = d._year;this->_month = d._month;this->_day = d._day;}return *this;//this这里指向的是d1,因为作用的对象就是d1,出了作用域this还在,所以能用引用返回//自定义类型不能传值拷贝,需要调用一个拷贝构造
}

赋值重载和拷贝构造的区别?

赋值,两个已经存在的对象进行拷贝
拷贝构造,一个已经存在对象,去初始化另一个要创建的对象

前置++和后置++重载

在类里,由于都有'++'操作符,所以为了区分,我们用' int '类型来区分

Date& operator++();//前置Date& Date::operator++()//改变自身,所以直接作用于this指针
{*this += 1;return *this;
}Date operator++(int);//后置Date Date::operator++(int)//仅对变量赋值作改变,所以需要用一个载体来进行数据的改变
{Date tmp(*this);*this += 1;return tmp;
}

顺序表类的实现及使用

struct SeqList
{
public:void PushBack(int x){_a[_size++] = x;}void CheckCapacity(){;}size_t size() const{return _size;}//打印void Print(){for (int i = 0; i < _size; i++){cout << _a[i] << endl;}}//读const int& operator[](size_t i) const //operator作用于类的对象{assert(i < _size);return _a[i];}//写int& operator[](size_t i)//operator作用于类的对象{assert(i < _size);return _a[i];}
private:int* _a=(int*)malloc(sizeof(int)*10);int _size=0;int _capacity=0;
};

在C语言中,我们实现一个顺序表数据的放入会及其麻烦,但是在C++中有了类,那么就可以极大的简化代码

int main()
{SeqList st;st.PushBack(1);st.PushBack(2);st.PushBack(3);st.PushBack(4);return 0;
}

访问——可以通过运算符重载实现

for (size_t i = 0; i < st.size(); i++)
{cout << st[i] << " ";//用运算符重载,这样不仅实现了特定位置数据的返回,还可以进行遍历cout << st.operator[](i) << endl;
}

通过流输出打印对象的内容

ostream& operator<<(ostream& out, const Date& d)//类的流插入
{out << d._year << "/" << d._month << "/" << d._day << endl;return out;
}

流输入输入对象的内容

istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

以上两个不做介绍,需要用到out相关类的知识

相关文章:

C++ 类和对象

面向过程/面向对象 C语言是面向过程&#xff0c;关注过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题 C是基于面对对象的&#xff0c;关注的是对象——将一件事拆分成不同的对象&#xff0c;依靠对象之间的交互完成 引入 C语言中结构体只能定义…...

c# 此程序集中已使用了资源标识符

严重性 代码 说明 项目 文件 行 禁止显示状态 错误 CS1508 此程序集中已使用了资源标识符“BMap.NET.WindowsForm.BMapControl.resources” BMap.NET.WindowsForm D:\MySource\Decompile\BMap.NET.WindowsForm\CSC 1 活动 运行程序时&a…...

WPF实战学习笔记30-登录、注册服务添加

登录、注册服务添加 添加注册数据类型添加注册UI修改bug UserDto的UserName更改为可null类型Resgiter 添加加密方法修改控制器 添加注册数据类型 添加文件MyToDo.Share.Models.ResgiterUserDto.cs using System; using System.Collections.Generic; using System.Linq; us…...

GDAL C++ API 学习之路 OGRGeometry 圆弧类 OGRCircularString

OGRCircularString Class <ogrsf_frmts.h> OGRCircularString 类是 OGR 几何库中的一个类&#xff0c;用于表示圆弧字符串&#xff08;circular string&#xff09;类型的几何图形。圆弧字符串是由一系列圆弧段组成的几何图形&#xff0c;每个圆弧段由三个点定义…...

机器学习:异常检测

问题定义 anomaly&#xff0c;outlier&#xff0c; novelty&#xff0c; exceptions 不同的方法使用不同的名词定义这类问题。 应用 二分类 假如只有正常的数据&#xff0c;而异常的数据的范围非常广的话&#xff08;无法穷举&#xff09;&#xff0c;二分类这些不好做。另外就…...

flask中的蓝图

flask中的蓝图 在 Flask 中&#xff0c;蓝图&#xff08;Blueprint&#xff09;是一种组织路由和服务的方法&#xff0c;它允许你在应用中更灵活地组织代码。蓝图可以大致理解为应用或者应用中的一部分&#xff0c;可以在蓝图中定义路由、错误处理程序以及静态文件等。然后可以…...

Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台免费搭建

Java版知识付费-轻松拥有知识付费平台 多种直播形式&#xff0c;全面满足直播场景需求 公开课、小班课、独立直播间等类型&#xff0c;满足讲师个性化直播场景需求&#xff1b;低延迟、双向视频&#xff0c;亲密互动&#xff0c;无论是互动、答疑&#xff0c;还是打赏、带货、…...

uniapp 瀑布流 (APP+H5+微信小程序)

WaterfallsFlow.vue <template><view class"wf-page" :class"props?.paddingC ? paddingC : "><!-- left --><view><view id"left" ref"left" v-if"leftList.length"><viewv-for…...

医疗小程序:提升服务质量与效率的智能平台

在医疗行业&#xff0c;公司小程序成为提高服务质量、优化管理流程的重要工具。通过医疗小程序&#xff0c;可以方便医疗机构进行信息传播、企业展示等作用&#xff0c;医疗机构也可以医疗小程序提供更便捷的预约服务&#xff0c;优化患者体验。 医疗小程序的好处 提升服务质量…...

ComPDFKit 转档SDK OCR表格识别功能

我们非常高兴地宣布&#xff0c;适用于 Windows、iOS、Android 和服务器的 ComPDFKit 转档SDK 1.8.0 现已发布&#xff01;在该版本中&#xff0c;OCR 功能支持了表格识别&#xff0c;优化了OCR文字识别率。PDF to HTML 优化了html 文件结构&#xff0c;使转换后的 HTML 文件容…...

华为OD机考--阿里巴巴黄金箱

题目内容 贫如洗的樵夫阿里巴巴在去砍柴的路上&#xff0c;无意中发现了强盗集团的藏宝地&#xff0c;藏宝地有编号从0~N的箱子每个箱子上面贴有一个数字箱子中可能有一个黄金宝箱。 黄金宝箱满足排在它之前的所有箱子数字和等于排在它之后的所有箱子数字之和; 一个箱子左边部分…...

mybatis-config.xml-配置文件详解

文章目录 mybatis-config.xml-配置文件详解说明文档地址:配置文件属性解析properties 属性应用实例 settings 全局参数定义应用实例 typeAliases 别名处理器举例说明 typeHandlers 类型处理器environments 环境environment 属性应用实例 mappers配置 mybatis-config.xml-配置文…...

【雕爷学编程】MicroPython动手做(18)——掌控板之声光传感器

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…...

Ribbon源码

学了feign源码之后感觉&#xff0c;这部分还是按运行流程分块学合适。核心组件什么的&#xff0c;当专业术语学妥了。序章&#xff1a;认识真正のRibbon 但只用认识一点点 之前我们学习Ribbon的简单使用时&#xff0c;都是集成了Eureka-client或者Feign等组件&#xff0c;甚至在…...

Linux下在终端输入密码隐藏方法

Linux系统中&#xff0c;如何将在终端输入密码时将密码隐藏&#xff1f; 最近做简单的登录界面时&#xff0c;不做任何操作的话&#xff0c;在终端输入密码的同时也会显示输入的密码是什么&#xff0c;这样对于隐蔽性和使用都有不好的体验。那么我就想到将密码用字符*隐藏起来…...

【ARM 常见汇编指令学习 3 -- ARM64 无符号位域提取指令 UBFX】

文章目录 ARM64 无符号位域提取指令 上篇文章&#xff1a;ARM 常见汇编指令学习 2 – 存储指令 STP 与 LDP 下篇文章&#xff1a;ARM 常见汇编指令学习 4 – ARM64 比较指令 cbnz 与 b.ne 区别 ARM64 无符号位域提取指令 在代码中如何监控寄存器的某1bit&#xff0c; 或者某几…...

求分享如何批量压缩视频的容量的方法

视频内存过大&#xff0c;不但特别占内存&#xff0c;而且还会使手机电脑出现卡顿的现象&#xff0c;除此之外&#xff0c;如果我们想发送这些视频文件可能还会因为内存太大无法发送。因此&#xff0c;我们可以批量地压缩视频文件的内存大小&#xff0c;今天小编要来分享一招&a…...

ChatGPT 是如何工作的:从预训练到 RLHF

欢迎来到人工智能的未来&#xff1a;生成式人工智能&#xff01;您是否想知道机器如何学习理解人类语言并做出相应的反应&#xff1f;让我们来看看ChatGPT ——OpenAI 开发的革命性语言模型。凭借其突破性的 GPT-3.5 架构&#xff0c;ChatGPT 席卷了世界&#xff0c;改变了我们…...

KafKa脚本操作

所有操作位于/usr/local/kafka_2.12-3.5.1/bin。 rootubuntu2203:/usr/local/kafka_2.12-3.5.1/bin# pwd /usr/local/kafka_2.12-3.5.1/bin rootubuntu2203:/usr/local/kafka_2.12-3.5.1/bin# ls connect-distributed.sh kafka-delegation-tokens.sh kafka-mirror-mak…...

【自动化运维】playbook剧本

目录 一、Ansible 的脚本 playbook 剧本1.1playbooks的组成 二、剧本编写实验2.1定义、引用变量2.2使用远程主机sudo切换用户2.3whenn条件判断2.4迭代 三、Templates 模板四、Tags模板 一、Ansible 的脚本 playbook 剧本 1.1playbooks的组成 &#xff08;1&#xff09;Tasks&…...

java中双引号和单引号的区别

起因 刷题的时候&#xff0c;有判断是否相同的情况&#xff0c;然后我发现单引号和双引号在上的表现不一样&#xff0c;所以记录一下。 解释 在Java中&#xff0c;双引号&#xff08;" "&#xff09;和单引号&#xff08;’ &#xff09;在使用上有很重要的区别&a…...

jenkinsfile指定jenkins流水线的构建号

背景 升级Jenkins过程中不小心导致流水线配置文件job目录丢失, 重新配置流水线后所有流水线构建号码都从1开始构建了, 然而我们的产品关联了jenkins构建号,重新从1 构建会导致各种问题. 解决方案 在Jenkinsfile文件中指定流水线的构建号为一个不存在的数字, 这样就不会冲突了…...

微信小程序:实现提示窗确定,取消执行不同操作(消息提示确认取消)showModal

效果 代码 wx.showModal({title: 提示,content: 是否确认退出,success: function (res) {if (res.confirm) {console.log(用户点击确定)} else if (res.cancel) {console.log(用户点击取消)}}})...

深度学习论文: Q-YOLO: Efficient Inference for Real-time Object Detection及其PyTorch实现

深度学习论文: Q-YOLO: Efficient Inference for Real-time Object Detection及其PyTorch实现 Q-YOLO: Efficient Inference for Real-time Object Detection PDF: https://arxiv.org/pdf/2307.04816.pdf PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代…...

解读随机森林的决策树:揭示模型背后的奥秘

一、引言 随机森林[1]是一种强大的机器学习算法&#xff0c;在许多领域都取得了显著的成功。它由多个决策树组成&#xff0c;而决策树则是构建随机森林的基本组件之一。通过深入解析决策树&#xff0c;我们可以更好地理解随机森林模型的工作原理和内在机制。 决策树是一种树状结…...

OceanMind海睿思获评中国信通院“内审数字化产品评测”卓越级(最高级)!

2023年7月27日&#xff0c;由中国内部审计协会、中国通信标准化协会指导&#xff0c;中国信息通信研究院主办的第二届数字化审计论坛在北京成功召开。 大会聚焦内部审计数字化领域先进实践、研究成果、行业发展举措&#xff0c;重磅发布了多项内部审计数字化领域的最新研究和实…...

TPlink云路由器界面端口映射设置方法?快解析内网穿透能实现吗?

有很多网友在问&#xff1a;TPlink路由器端口映射怎么设置&#xff1f;因为不懂端口映射的原理&#xff0c;所以无从下手&#xff0c;下面小编就给大家分享TPlink云路由器界面端口映射设置方法&#xff0c;帮助大家快速入门TP路由器端口映射设置方法。 1.登录路由器管理界面&a…...

css3的filter图片滤镜使用

业务介绍 默认&#xff1a;第一个图标为选中状态&#xff0c;其他三个图标事未选中状态 样式&#xff1a;选中状态是深蓝&#xff0c;未选中状体是浅蓝 交互&#xff1a;鼠标放上去选中&#xff0c;其他未选中&#xff0c;鼠标离开时候保持当前选中状态 实现&#xff1a;目前…...

❤️创意网页:打造炫酷网页 - 旋转彩虹背景中的星星动画

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…...

react常用知识点

React是一个用于构建用户界面的JavaScript库。以下是React常用的知识点&#xff1a; 组件&#xff1a;React将用户界面分解成小而独立的组件&#xff0c;每个组件都有自己的状态和属性&#xff0c;并且可以通过组合这些组件来构建复杂的用户界面。 // 函数组件示例 function We…...

烟台搭建网站建设制作/怎样做关键词排名优化

现在很多的朋友都应该见过VR全景&#xff0c;但是不知道VR全景具体是什么&#xff1f;是怎么做的&#xff1f;有什么作用&#xff1f;今天小粉就给大家说一下。VR全景是当今最新颖的宣传展示的手段&#xff0c;代表了科技的更进一步&#xff0c;随着5G的发展&#xff0c;VR全景…...

网站的营销与推广/网站收录查询工具

bitsCN.comMySQL提供标准的SQL模式匹配&#xff0c;以及一种基于象Unix实用程序如vi、grep和sed的扩展正则表达式模式匹配的格式。SQL的模式匹配允许你使用“_”匹配任何单个字符&#xff0c;而“%”匹配任意数目字符(包括零个字符)。在 MySQL中&#xff0c;SQL的模式缺省是忽略…...

网站建设要注意/网络营销和网络推广

上一篇讲到游戏运作的原理是非常简单的,实现一个五子棋很可能只需要使用操作系统提供的原生开发环境,简单的做一些代码实现即可完成,我自己在大学的时候就找到了一个一步一步教你使用Windows API开发一个五子棋游戏的教程,需要用到的也只需要安装一个visual studio顺便安装…...

编程软件手机/关键词seo排名怎么选

除了是动手的程序员&#xff0c;我还是定制软件开发公司Teamed.io的联合创始人兼CTO。 在我们合作的所有项目中&#xff0c;我都扮演着技术和管理领导者的角色。 我为有兴趣雇用我和/或我的团队的人写了这篇文章。 本文将演示从第一天到项目结束时您选择与我们合作的过程 。 …...

做网站的费用属于什么费用/互联网推广广告

1.在很多时候我们在网站上应用的时候都会用到nginx&#xff0c;由于我们是java开发者&#xff0c;不可避免的是我们需要在我们的tomcat的工程中应用到nginx&#xff0c;这里的应用可以是请求转发&#xff0c;负载均衡&#xff0c;反向代理&#xff0c;配置虚拟站点等。2.当然很…...

自己建网站怎么赚钱/海南seo顾问服务

&#x1f447;&#x1f447;关注后回复 “进群” &#xff0c;拉你进程序员交流群&#x1f447;&#x1f447;来源&#xff1a;cnblogs.com/peiyu1988.html01前言2019年初&#xff0c;我通过一整天的笔试及面试加入一家&#xff08;某一线城市国资委全资控股&#xff09;某集团…...