c++初阶------类和对象(六大默认构造函数的揭破)

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


类和对象

  • **作者前言**
  • 默认成员函数
  • 类的空类
  • 构造函数
      • 内置类型和自定义类的的自动初始化情况
  • 析构函数
  • 拷贝函数
    • 拷贝函数的特征
  • 赋值运算符重载
    • 运算符重载
    • 赋值运算符重载
    • cout的流提取(<<)
  • const成员函数
  • 取地址及const取地址操作符重载

默认成员函数

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
在这里插入图片描述

类的空类

我们在写类的成员变量和成员函数,我们实例化一个对象,有时候就会忘记实例化,直接使用成员函数,这个在C语言会报错,在c++就不会报错,但是值是随机的

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Stru
{
public:void Fun(){cout << _a << endl;cout << _b << endl;}
private:int* _a;int _b;
};
int main()
{Stru str;str.Fun();return 0;
}

有啥方法可以解决呢?答案是构造函数

构造函数

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

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任
务并不是开空间创建对象,而是初始化对象。
其特征如下:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Stru
{
public:Stru(){_a = 200;_b = 100;}Stru(int a, int b){_a = a;_b = b;}void Fun(){cout << _a << endl;cout << _b << endl;}
private:int _a;int _b;
};
int main()
{Stru str;str.Fun();Stru st1(1, 1);st1.Fun();return 0;
}

这里有两种写法,一种是直接使用自己定义的,一种是使用传入的参数,需要注意的是 第一种写法不用加(),因为加了就相当于是函数声明了。
注意:可以使用函数重载,但是要注意是否有冲突,缺省参数和无参数是否存在歧义。

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

c++编译器默认构造函数:一般情况内置类型不会进行初始化,自定义类型的初始化会自动调用默认构造函数。

内置类型和自定义类的的自动初始化情况

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Stack
{
public:Stack(){_a = (int *)malloc(sizeof(int) * 4);if (_a == nullptr){perror("malloc");return;}_top = 0;//栈顶元素的下一个_capacity = 4;}//插入void Stackpushback(int elemest){if (_top == _capacity){int size = _capacity > 0 ? 2 * _capacity : 4;int* tmp = (int*)realloc(_a, sizeof(int) * size);if (tmp == nullptr){perror("realloc");return;}_a = tmp;_capacity = size;}_a[_top++] = elemest;}//删除void Stackpop(){if(_top)_top--;}//栈顶元素int Stacktop(){if(_top)return _a[_top - 1];return INT_MAX;}//是否为空bool Stackempty(){return _top;}private:int* _a;int _top;int _capacity;};
class StackQueue
{
private://入数据的栈Stack pushstack;//出数据的栈Stack popstack;int size;};
int main()
{Stack mystack;StackQueue mq;return 0;
}

在这里插入图片描述
可以看到size没有初始化就赋值了0,因为在vs2019优化过了,我们一般就认为是没有初始化化的,

内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认
注意一下:指针类型都是内置类型

但是在C++11就增加了补丁
**内置类型成员变量在类中声明时可以给默认值。**本质上还是声明,定义是要给空间的
在这里插入图片描述
总结:

  1. 我们不写构造函数,编译器会自己写一个构造函数,这个值是随机值,这个函数叫做默认构造
  2. 无参数构造函数可以叫默认构造
  3. 全缺省函数也叫默认构造
  4. 这三种不能同时存在,只能存在一个

析构函数

前面我们学习了构造函数,主要用于初始化对象
这里我们学习析构函数

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

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

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
    函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public:~Date(){cout << "调用析构函数" << endl;}Date(){_a = 20;}
private:int _a = 30;
};
int main()
{Date time;return 0;
}

在这里插入图片描述
这个析构函数我们可以运用于经常忘记的内存释放的地方,比如栈使用结束后,空间释放

在这里插入图片描述
注意:析构调用完才销毁对象

拷贝函数

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public:Date(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;
};
void Fun(Date d)
{d.print();
}
int main()
{Date d1(2024, 1, 4);Fun(d1);return 0;
}

这种是一种浅拷贝,但是当浅拷贝运用到某些场景就会带来不必要的麻烦

在这里插入图片描述
当Fun函数运行的时候是有可能更改对应地址的值的

拷贝函数的特征

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

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,
    因为会引发无穷递归调用。
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}~Date(){cout << "调用析构函数" << endl;}Date(const Date& dd){_year = dd._year;_month = dd._month;_day = dd._day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 1, 3);Date d2(d1);return 0;
}

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

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}~Date(){cout << "调用析构函数" << endl;}Date(const Date& dd){_year = dd._year;_month = dd._month;_day = dd._day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
void Fun(Date d)
{d.print();
}
int main()
{Date d1(2024, 1, 3);Date d2(d1);Fun(d1);return 0;
}

在这里插入图片描述

这里的dd是是d1的引用,因为形参是实参的临时拷贝,这里的this指针指向的是d,
这个图也可以解释出,为啥拷贝函数传值会陷入无限的循环

  1. 深拷贝
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Stack
{
public:Stack(int capacity = 3){_a = (int*)malloc(sizeof(int) * capacity);if (_a == NULL){perror("malloc");exit(-1);}_capacity = capacity;_top = 0;}//深拷贝Stack(const Stack& st){_a = (int*)malloc(sizeof(int) * st._capacity);if (_a == NULL){perror("malloc");exit(-1);}_capacity = st._capacity;_top = st._top;}
private:int *_a;int _top;int _capacity;
};
int main()
{Stack d1(5);Stack d2(d1);return 0;
}

在这里插入图片描述
在这里插入图片描述
创建两个一模一样的空间,深拷贝就是要进行一比一的复制,浅拷贝在这里会公用一块地址,
浅拷贝的示意图:
在这里插入图片描述
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请
时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
简单的来说,如果涉及到地址就要考虑一下是否需要深拷贝,如是简单的拷贝值的化,拷贝函数可以写也可以不写,不写的话,编译器会默认构造一个拷贝函数,(浅拷贝)

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Stack
{
public://构造函数Stack(int capacity = 3){_a = (int*)malloc(sizeof(int) * capacity);if (_a == NULL){perror("malloc");exit(-1);}_capacity = capacity;_top = 0;}//深拷贝Stack(const Stack& st){_a = (int*)malloc(sizeof(int) * st._capacity);if (_a == NULL){perror("malloc");exit(-1);}_capacity = st._capacity;_top = st._top;}//析构函数~Stack(){free(_a);cout << "释放了空间" << endl;_a = nullptr;_top = 0;_capacity = 0;}
private:int *_a;int _top;int _capacity;
};
class MyQueue
{
private:Stack _d1;Stack _d2;int _size = 0;};
int main()
{MyQueue q1;MyQueue q2(q1);//调用了拷贝构造return 0;
}

这里的情况就是q1会开辟成员变量的空间,调用了MyQueue的默认构造函数,然后_d1和_d2调用Stack的自定义的默认构造.
在这里插入图片描述
在这里插入图片描述
q1和q2之间的联系,之间调用的是拷贝函数,调用MyQueue的默认拷贝构造函数,而Stack调用的就是自定义的拷贝构造函数

一些场合
自定义当返回值

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Stack
{
public://构造函数Stack(int capacity = 3){_a = (int*)malloc(sizeof(int) * capacity);if (_a == NULL){perror("malloc");exit(-1);}_capacity = capacity;_top = 0;}//深拷贝Stack(const Stack& st){cout << "拷贝" << endl;_a = (int*)malloc(sizeof(int) * st._capacity);if (_a == NULL){perror("malloc");exit(-1);}_capacity = st._capacity;_top = st._top;}//析构函数~Stack(){free(_a);cout << "释放了空间" << endl;_a = nullptr;_top = 0;_capacity = 0;}
private:int *_a;int _top;int _capacity;
};
class MyQueue
{
private:Stack _d1;Stack _d2;int _size = 0;};
Stack fun()
{Stack d1;return  d1;
}
int main()
{fun();return 0;
}

在这里插入图片描述
可以看出返回值,不会直接返回d1,而是d1的拷贝,也就是d1的临时对象
如果换成

Stack& fun()
{static Stack d1;return  d1;
}

在这里插入图片描述

在这里插入图片描述
可以把图中的Test函数换成我们的fun函数,看懂了就可以了。

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

赋值运算符重载

运算符重载

前面我们学习C语言,知道内置类型(int 、char…)可以直接使用各种运算符,但是自定义类型是不可以的

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public://构造函数Date(int year, int month, int day){cout << "构造函数" << endl;_year = year;_month = month;_day = day;}//浅拷贝Date(const Date& d1){cout << "拷贝" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}//析构函数~Date(){cout << "析构函数" << endl;}int _year;int _month;int _day;
};
//相等
bool Equal(Date x, Date y)
{return x._year == y._year&& x._month == y._month&& x._day == y._day;
}
//大于
bool Greater(Date x, Date y)
{if (x._year > y._year){return true;}else if (x._year == y._year && x._month > y._month){return true;}else if (x._year == y._year && x._month == y._month && x._day > y._day){return true;}elsereturn false;
}
int main()
{Date a(2024,1,3);Date b(2024, 1, 5);cout << Equal(a, b) << endl;return 0;
}

在这里插入图片描述
我们如果要比较这些自定义类型就有可能要写成函数,但是不太方便,所以c++改变了一些

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

//相等
bool operator==(Date x, Date y)
{return x._year == y._year&& x._month == y._month&& x._day == y._day;
}
//大于
bool operator>(Date x, Date y)
{if (x._year > y._year){return true;}else if (x._year == y._year && x._month > y._month){return true;}else if (x._year == y._year && x._month == y._month && x._day > y._day){return true;}elsereturn false;
}

函数名就更改过成这样的,使用这个函数方法有两种,一种是直接写函数名直接调用

另一种
在这里插入图片描述
这里使用a ==b 就是 operator==(x, y)的简写,需要注意的是 << 的优先级比 == 、>…高。

函数重载和运算符重载两者毫不相关,函数重载允许参数不同的同名函数,而运算符重载允许自定义类型直接使用运算符
我们还可以在类里面写

#include<iostream>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public://构造函数Date(int year, int month, int day){cout << "构造函数" << endl;_year = year;_month = month;_day = day;}//浅拷贝Date(const Date& d1){cout << "拷贝" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}//析构函数~Date(){cout << "析构函数" << endl;cout << _year << '-' << _month << '-' << _day << endl;}//相等bool operator==(Date& y){return _year == y._year&& _month == y._month&&_day == y._day;}
private:int _year;int _month;int _day;//大于bool operator>(Date& y){if (_year > y._year){return true;}else if (_year == y._year && _month > y._month){return true;}else if (_year == y._year && _month == y._month && _day > y._day){return true;}elsereturn false;}void exchengtime(int* a){if ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0))a[2] = 29;elsea[2] = 28;}Date& operator+=(int day)//这里返回引用是防止对象拷贝返回{int a[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };_day += day;exchengtime(a);while (_day > a[_month]){_day -= a[_month];_month++;if (_month > 12){_year++;exchengtime(a);_month %= 12;}}return *this;}
};
int main()
{Date a(2024,1,3);Date b(2024, 1, 5);cout << (a == b) << endl; // a.operator==(b)  -> a.operator==(&a, b)a += 363;return 0;
}

赋值运算符重载

有时候就是我们会看见到i = 5; j = 20;
我们有时需要对对象进行赋值

#include<iostream>
//using namespace std;
using std::cout;
using std::endl;
using std::cin;
class Date
{
public://构造函数Date(int y, int m, int d){_year = y;_month = m;_day = d;}//拷贝Date(const Date& d1){_year = d1._year;_month = d1._month;_day = d1._day;}//析构函数~Date(){cout << _year << endl;cout << _month << endl;cout << _day << endl;}Date& operator=(const Date& d1){//防止自己给自己赋值(特别是深拷贝的时候)if (this != &d1){_year = d1._year;_month = d1._month;_day = d1._day;}return *this;}
private:int _year;int _month;int _day;
};
int main()
{int a = 10;cout << a << endl;Date d1(2024, 1, 15);Date d2(2024,1,16);Date d3(2024,1,17);d3 = d1 = d2;return 0;
}

这样就可以不使用到拷贝构造,
还需要注意的是operator=是默认成员函数,不写编译器会默认生成,跟拷贝构造的行为类似,对内置类型进行值拷贝,自定义类型调用他的赋值

赋值运算符重载格式:
1. 参数类型:const T&,传递引用可以提高传参效率
2. 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
3. 检测是否自己给自己赋值
4. 返回*this :要复合连续赋值的含义
全部代码总和:
test.h

#pragma once
#include<iostream>
#include<assert.h>
using std::cout;
using std::endl;
using std::cin;
class Date
{
public://构造函数Date(int y = 2024, int m = 1, int d = 15);//拷贝Date(const Date& d1);//析构函数~Date();int Getmonthday(int year, int month);bool operator<(const Date& d1);bool operator>(const Date& d1);bool operator==(const Date& d1);bool operator<=(const Date& d1);bool operator>=(const Date& d1);Date& operator=(const Date& d1);Date& operator+=(int day);Date operator+(int day);private:int _year;int _month;int _day;
};

test.cpp

#include"Date.h"
Date::Date(int y, int m, int d)
{_year = y;_month = m;_day = d;if (_year < 0 || _month < 1 || _month >12 || _day > Getmonthday(_year, _month) || _day < 0){cout << _year << "-" << _month << "-" << _day << endl;cout << "非法日期" << endl;exit(-1);}}
int Date::Getmonthday(int year, int month)
{int monthday[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){monthday[2] = 29;}return monthday[month];}
//拷贝
Date::Date(const Date& d1)
{_year = d1._year;_month = d1._month;_day = d1._day;
}
//析构函数
Date::~Date()
{cout << _year << endl;cout << _month << endl;cout << _day << endl;
}
Date& Date::operator=(const Date& d1)
{//防止自己给自己赋值(特别是深拷贝的时候)if (this != &d1){_year = d1._year;_month = d1._month;_day = d1._day;}return *this;
}
Date& Date::operator+=(int day)
{*this = *this + day;return *this;
}
Date Date::operator+(int day)
{Date tmp(*this);int monthday[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };tmp._day += day;if ((tmp._year % 4 == 0 && tmp._year % 100 != 0) || (tmp._year % 400 == 0)){monthday[2] = 29;}else{monthday[2] = 28;}while (tmp._day - monthday[tmp._month] > 0){tmp._day -= monthday[tmp._month];tmp._month++;if (tmp._month > 12){tmp._month %= 12;tmp._year++;if ((tmp._year % 4 == 0 && tmp._year % 100 != 0) || (tmp._year % 400 == 0)){monthday[2] = 29;}else{monthday[2] = 28;}}}return tmp;}
bool Date::operator<(const Date& d1)
{if (_year < d1._year){return true;}if (_year == d1._year && _month < d1._month){return true;}if (_year == d1._year && _month == d1._month && _day < d1._day){return true;}return false;}
bool Date::operator==(const Date& d1)
{return _year == d1._year && _month == d1._month && _day == d1._day;
}
bool Date::operator<=(const Date& d1)
{return (*this) < d1 || (*this) == d1;
}
bool Date::operator>(const Date& d1)
{return !((*this) <= d1);
}
bool Date::operator>=(const Date& d1)
{return !((*this)< d1);
}

test1.cpp

#include"Date.h"
int main()
{Date d1(2024, 1, 3);Date d2(2024,1,16);Date d3(2020,1,17);int a = d1 == d2;cout << a << endl;a = d1 >= d2;cout << a << endl;a = d1 <= d2;cout << a << endl;d3 += 366;return 0;
}

需要注意的是代码中的**operator+(int day)**函数 和 **operator+=(int day)**函数的效率是很低效的
在这里插入图片描述
这张图里面可以说明,上图的代码的对象拷贝是次数多,造成了效率的低效,
对象的赋值进行了一次拷贝对象
*this + day 创建了一个临时的 Date 对象,表示当前对象的值加上 day 天的结果。*this = … 将上面提到的临时对象的值赋给了当前对象 *this。这个赋值操作涉及到对象的拷贝。赋值操作符 = 的行为是根据类的定义而定的,,它会执行对象的成员逐个拷贝。

实现 - 和-=
这里的实现方法和上面的方法是不一样的,

Date& Date::operator-=(int day)
{_day -= day;int monthday[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0)){monthday[2] = 29;}else{monthday[2] = 28;}while (_day <= 0){_month--;if (_month <= 0){_year--;if ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0)){monthday[2] = 29;}else{monthday[2] = 28;}_month = 12;}_day += monthday[_month];}return *this;}
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;}

可以看看这里,这里的情况就是,在operator-函数里面套用operator-=,这样的效率就变高了

实现++
前置++

Date& Date::operator+=(int day)
{_day += day;int monthday[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0)){monthday[2] = 29;}else{monthday[2] = 28;}while (_day - monthday[_month] > 0){_day -= monthday[_month];_month++;if (_month > 12){_month %= 12;_year++;if ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0)){monthday[2] = 29;}else{monthday[2] = 28;}}}return *this;
}
Date& Date::operator++()
{*this += 1;return *this;
}

后置++
后置++不能和上面写的一样,因为要构成重载,我们只能从参数那里下手,不然就会无法构成重载,

Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;}

编译器链接的时候,函数的名字只和函数的名字和函数的参数类型有关,跟函数的形参名字没有关系。
就是说c++的函数在链接的时候有自己的函数名修饰规则,
在这里插入图片描述

cout的流提取(<<)

小解释
我们在使用std::cout的打印不同类型是不需要和C语言写出格式符,
在这里插入图片描述
我们使用iostram头文件,主要就是里面定义了对象,
我们顺着点击
在这里插入图片描述
在这里插入图片描述
就会看到下图
在这里插入图片描述
可以看到定义了很多函数,简单的来说就是函数重载,类型匹配,进而就解释了cout支持流提取,cout是一个对象,
那我们来函数重载一个流提取

void Date::operator<<(ostream& out)
{out << _year << "-" << _month << "-" << _day << endl;
}

out相当于我们把std::cou传入,

cout的小例子
我们有时候在使用cout打印一个自定义类型的变量时,会出错,例如

Date A;//Date是一个类,A是对象
cout<<A;

如果我们这样写的话会报错,因为cout的类方法是没有写这样的双操作数运算符符重载的,
如果我们按照刚刚写的日期类的流提取的函数重载,
可以这样
在这里插入图片描述
这样可以正常输出,因为双操作数运算符的第一个参数是左操作数,第二个参数是右操作数,我们这个日期类的流提取函数重载的第一个参数就是this这个隐藏的参数
void Date::operator<<(Date * const this ,ostream& out)
这样实现的话虽然没有错,但是不方便理解,运算符重载也没有规定在类之外不能进行重载,所以我们可以在类外进行运算符重载

void operator<<(ostream& out, const Date& A)
{out << A._year << "-" << A._month << "-" << A._day << endl;
}

需要把Date的类成员进行public权限才能成功运行
友元函数
或者我们可以写一个友元函数
友元函数 :既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数 ,统称为友元函数

//友元函数
friend void operator<<(ostream& out, const Date& A)
{out << A._year << "-" << A._month << "-" << A._day << endl;
}

这个函数是在类定义的,但是不是成员函数,
如果在类之外定义不用加Data::这个

我们还知道

cout << a<< b<<endl;

支持这样的上面写的不支持,如果要改动,就把返回类型改为ostream&
在这里插入图片描述
这样就可以支持了

流插入

//流插入friend istream& operator>>(istream& out, Date& A){out >> A._year;out >> A._month;out >> A._day;cout << A;return out;}

小总结
一般的运算符一般实现成成员函数,而流插入和流提取必须实现全局,这样才能让流对象作为第一个参数.

流是为解决自定义类型的输入和输出问题,C语言的printf 、scanf只能解决内置类型,无法解决自定义类型的输入输出问题

const成员函数

我们在C语言知道,const修饰的变量是改变不了,如果是强行改变那就另当别论了,需要注意的是权限可以平移和缩小,但是不能放大,也就是说,const修饰的变量可以传参给const修饰的变量(权限平移), 非const修饰的变量可以传参给const修饰的变量(权限缩小), 但是不能const修饰的变量可以传参给非const修饰的变量(权限放大)
那么在cpp中是怎么使用const,会有怎么样的效果呢?

const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
在这里插入图片描述
这里我定义一个const修饰的对象,一个是没有const修饰的对象,

当我们写出下面代码
在这里插入图片描述
会发现有报错,为啥会这样呢

在这里插入图片描述
在这个运算符重载中,this这个参数是没有被const修饰过的,被const修饰过的A使用没有被const的成员函数,就会报错,
所以这里有个结论:

  1. const对象,不能调用非const成员函数,只能调用const成员函数(权限的平移)
  2. 普通对象可以调用非const成员函数,也可以调用const成员函数(权限的平移和缩小)

取地址及const取地址操作符重载

//取地址操作符重载
Date* Date::operator&()
{return this;
}
//主要为了适应const对象取地址操作符重载,
const Date* Date::operator&()const
{return this;
}

这两个是默认成员函数一般不用重新定义 ,编译器默认会生成。
除非因为自己有想法,可以进行重载,这样可以恶搞一下其他人

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

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

相关文章

1.JavaWebJava基础加强[万字长文]-Junit、反射、注解核心知识点梳理

导语&#xff1a; 一、Junit单元测试 1.Junit测试概述 2.Junit使用步骤 3.Junit_Before&After 二、反射 1.反射概述 2.反射获取字节码Class对象的三种方式 3.Class对象功能概述 4.Class对象功能_获取Field 5.Class对象功能_获取Constructor 6.Class对象功能_获取…

Kakarot:当今以太坊的未来

1. 引言 前序博客&#xff1a; Kakarot&#xff1a;部署在Starknet上的ZK-EVM type 3 随着 Kakarot zkEVM 即将发布测试网&#xff0c;想重申下 Kakarot zkEVM 的愿景为&#xff1a; 为什么在rollup空间中还需要另一个 zkEVM&#xff1f; 开源代码见&#xff1a; https:/…

Anaconda prompt运行打开jupyter notebook 指令出错

一、打不开jupyter notebook网页 报错如下&#xff1a; Traceback (most recent call last): File “D:\anaconda3\lib\site-packages\notebook\traittypes.py”, line 235, in _resolve_classes klass self._resolve_string(klass) File “C:\Users\DELL\AppData\Roaming\Py…

软考 系统分析师系列知识点之系统性能评估(3)

接前一篇文章&#xff1a;软考 系统分析师系列知识点之系统性能评估&#xff08;2&#xff09; 所属章节&#xff1a; 第6章. 系统配置与性能评价 第8节. 系统性能评估 6.8.2 经典评估方法 在计算机技术的发展过程中&#xff0c;性能评估的常用方法有&#xff1a;时钟频率法、…

安全SCDN详解

随着互联网发展&#xff0c;越来越多的人都会使用到网站&#xff0c;个人网站、企业网站&#xff0c;各类大大小小的网站影响到人们的生活方方面面。许多站长都有使用或是听起过CDN&#xff0c;而SCDN&#xff0c;全称为“Security Content Delivery Network”&#xff0c;即安…

【Java设计模式】八、装饰者模式

文章目录 0、背景1、装饰者模式2、案例3、使用场景4、源码中的实际应用 0、背景 有个快餐店&#xff0c;里面的快餐有炒饭FriedRice 和 炒面FriedNoodles&#xff0c;且加配菜后总价不一样&#xff0c;计算麻烦。如果单独使用继承&#xff0c;那就是&#xff1a; 类爆炸不说&a…

985硕的4家大厂实习与校招经历专题分享(part1)

先简单介绍一下我的个人经历&#xff1a; 985硕士24届毕业生&#xff0c;实验室方向:CV深度学习 就业&#xff1a;工程-java后端 关注大模型相关技术发展 校招offer: 阿里巴巴 字节跳动 等10 研究生期间独立发了一篇二区SCI 实习经历:字节 阿里 京东 B站 &#xff08;只看大厂…

非科班如何系统自学Python?

在数字化时代&#xff0c;Python如一位“全能选手”在编程语言的舞台上大放异彩&#xff0c;无论是数据科学、网站开发还是人工智能&#xff0c;它都能游刃有余地驾驭各种挑战。正因如此&#xff0c;越来越多的人渴望掌握这门兼具强大功能和优雅简洁的语言。 目录 引言 开始 …

okHttp MediaType MIME格式详解

一、介绍 我们在做数据上传时&#xff0c;经常会用到Okhttp的开源库&#xff0c;okhttp开源库也遵循html提交的MIME数据格式。 所以我们经常会看到applicaiton/json这样的格式在传。 但是如果涉及到其他文件等就需要详细的数据格式&#xff0c;否则服务端无法解析 二、okHt…

【MySQL】数据库设计

目录 数据库设计基本任务 软件项目开发周期中数据库设计数据库设计的基本步骤解释需求分析需求分析的三个步骤&#xff1a;1.需求调查:2.分析数据字典内容定义数据的方法案例 3. 评审 概念结构设计概念模型概念结构设计E-R图概念模型组成元素&#xff1a;关系解释案例分析 逻辑…

【R包开发:入门】 简介+ 包的结构

简介 本书的目的是教你如何开发包&#xff0c;以便你可以写出自己的包&#xff0c;而不只是使用别人的包。 为什么要写一个包&#xff1f; 一个令人信服的理由是&#xff0c;你想要与他人分享代码。把你的代码打成一个包&#xff0c;可以方便他人使用&#xff0c;因为他们像你…

2024年 AI 辅助研发趋势

AI辅助是指人工智能&#xff08;AI&#xff09;技术被应用于辅助人们完成各种工作、任务或活动的过程。这种辅助可以包括提供智能建议、支持决策制定、自动化流程、增强人与机器之间的交互等。AI辅助通常旨在提高效率、减少人力成本、提升工作质量&#xff0c;并有助于拓展人类…

JavaScript实现遍历精灵图的效果

问题描述&#xff1a;通过JavaScript将精灵图里面的小图标给遍历出来。 关键代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width…

为什么猫咪主食冻干价格相差那么大?性价比高的主食冻干分享

养猫知识的不断普及&#xff0c;让主食冻干喂养逐渐受到铲屎官的青睐。但价格仍是部分铲屎官的顾虑。像我这样的资深猫友&#xff0c;早已开始尝试主食冻干喂养。虽然价格稍高&#xff0c;但其为猫咪带来的实际好处是远超其价格的。 作为一个多猫家庭的铲屎官&#xff0c;纯主食…

使用Echarts绘制中国七大区地图

先上效果图&#xff08;文字是否显示&#xff0c;显示什么字&#xff0c;各种颜色之类的&#xff0c;都能随便改&#xff09; 直接上完整代码 <!DOCTYPE html> <html style"height: 100%"><head><meta charset"utf-8" /></hea…

[最佳实践] Windows上构建一个和Linux类似的Terminal

感谢大佬批评指正&#xff0c;现已更新 preview Target&#xff1a;致力打造最赏心悦目Window下的终端&#xff0c;同时能够很接近Linux的使用习惯 key word&#xff1a;windows终端美化 windows terminal windows powershell 类似Linux下的Window终端 Window也能用ll windows…

如何减少AI中的偏见问题:八种方法避免AI偏见渗入模型

克服与避免 AI 偏见的八大方法 AI 中的算法偏见是一个普遍存在的问题&#xff0c;它虽然不可能完全消除&#xff0c;但却可以通过科学的方法积极地防止这种偏见。我们将在本文中围绕如何应对AI中的偏见问题展开深入的讨论。 您可能会回想起新闻中报道的一些存在偏见的算法示例…

JavaScript编程艺术:深度解析计算机程序的构造与解释【文末送书-32】

文章目录 JavaScript编程艺术&#xff1a;深度解析计算机程序的构造与解释第一部分&#xff1a;程序的构造第二部分&#xff1a;程序的解释第三部分&#xff1a;实例分析 计算机程序的构造和解释&#xff08;JavaScript版&#xff09;【文末送书-32】 JavaScript编程艺术&#…

利用MATLAB模拟点电荷的电场分布

一、实验目的&#xff1a; 1&#xff0e;熟悉点电荷的电场分布情况&#xff1b; 2&#xff0e;学会使用Matlab绘图&#xff1b; 二、实验内容&#xff1a; 1&#xff0e;根据库伦定律&#xff0c;利用Matlab强大的绘图功能画出单个点电荷的电场分布情况&#xff0c;包括电力…

小型园区配置示例——华为

小型园区配置示例 组网图形 图1 小型园区组网图 组网需求数据规划配置思路操作步骤业务验证 组网需求 某企业划分为A、B两个部门&#xff0c;企业中的用户可以相互访问&#xff0c;并且企业中的用户可访问Internet。 如图1所示&#xff0c;在小型园区中&#xff0c;S2700…