C++学习第五天

server/2025/2/1 23:10:22/

创作过程中难免有不足,若您发现本文内容有误,恳请不吝赐教。


提示:以下是本篇文章正文内容,下面案例可供参考


一、构造函数

问题1

        关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会
生成默认的构造函数。但是看起来默认构造函数又没什么用? d 对象调用了编译器生成的默认构造函数,但是d 对象 _year/_month/_day ,依旧是随机值。也就说在这里 编译器生成的 默认构造函数并没有什么用??
        
        解答:C++ 把类型分成内置类型 ( 基本类型 ) 和自定义类型。内置类型就是语言提供的数据类
型,如: int/char... ,自定义类型就是我们使用 class/struct/union 等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t 调用的它的默认构造函数。

示例1:

#include<iostream>
using namespace std;class A
{
public:A(int a){cout << "Aint(a)" << endl;_a = a;}
private:int _a;
};class Date
{
public://Date(int year, int month, int day)//{//	_year = year;//	_month = month;//	_day = day;//}
private://内置类型int _year;int _month;int _day;//自定义类型A _aa;
};int main()
{Date d1;
}

class A没有提供默认构造函数(不用参数就可以调用的构造).


示例2:

#include<iostream>
using namespace std;class A
{
public:A(int a = 0){cout << "A int(a)" << endl;_a = a;}
private:int _a;
};class Date
{
public://Date(int year, int month, int day)//{//	_year = year;//	_month = month;//	_day = day;//}
private://内置类型int _year;int _month;int _day;//自定义类型A _aa;
};int main()
{Date d1;
}

如果A不提供默认构造,需要使用初始化列表才能解决。当前的学习暂时先通过提供全缺省( A(int a = 0) )来解决这个问题.


简单来说:我们不写编译器生成的默认构造函数,对内置类型不做处理,对自定义类型会调用它的默认构造.


二、默认构造函数

  无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

       特点:不传参就可以调用的构造就是默认构造.


三、析构函数

   可以简单理解为与构造函数相反的函数,类比Destroy函数.


日期类不需要写析构函数,因为日期类没有资源需要清理,年、月、日属于对象,不需要清理。而像栈这种才需要清理,有指向资源才需要清理。


示例1:

  内置类型的成员,不做处理,造成堆上的空间没有释放,造成内存泄漏。

#include<iostream>
using namespace std;typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){_array[_size] = data;_size++;}/*~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}*/
private:// 内置类型DataType* _array;int _capacity;int _size;};void TestStack()
{Stack s;s.Push(1);s.Push(2);
}int main()
{TestStack();return 0;
}

示例2:

自定义类型的成员,会去调用他的析构。

#include<iostream>
using namespace std;class A
{
public:A(int a = 0){cout << "A(int a)" << endl;_a = a;}~A(){cout << "~A()" << endl;}
private:int _a;
};typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){_array[_size] = data;_size++;}/*~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}*/
private:// 内置类型DataType* _array;int _capacity;int _size;// 自定义类型A _aa;
};void TestStack()
{Stack s;s.Push(1);s.Push(2);
}int main()
{TestStack();return 0;
}


四、拷贝构造

日期类拷贝:

#include<iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}
private:// 内置类型DataType* _array;int _capacity;int _size;
};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 func1(Date d)
{d.Print();
}int main()
{Date d1(2025,1,8);func1(d1);return 0;
}


栈拷贝:

#include<iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}
private:// 内置类型DataType* _array;int _capacity;int _size;
};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 func1(Date d)
{d.Print();
}void func2(Stack s)
{}int main()
{Date d1(2025,1,8);func1(d1);Stack s1;func2(s1);return 0;
}


解决方法:

void func2(Stack& s)
{//使用引用
}

拷贝构造函数

        定义:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存

在的类类型对象创建新对象时由编译器自动调用
自定义类型传值传参要调用拷贝构造。

特征1:

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


特征2:

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

#include<iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){cout << "Date(Date& d)" << endl;_year = year;_month = month;_day = day;}Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << endl;}
private://内置类型int _year;int _month;int _day;
};int main()
{Date d1(2025,1,8);Date d2(d1);return 0;
}

	Date(Date d){//没有引用会引发无穷递归调用_year = d._year;_month = d._month;_day = d._day;}


#include<iostream>
using namespace std;typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}Stack(Stack& s){cout << "Stack(Stack& s)" << endl;// 深拷贝_array = (DataType*)malloc(sizeof(DataType) * s._capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}memcpy(_array, s._array, sizeof(DataType) * s._size);_size = s._size;_capacity = s._capacity;}void Push(DataType data){_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}
private:// 内置类型DataType* _array;int _capacity;int _size;
};void func2(Stack s)
{s.Push(1);
}int main()
{Stack s1;func2(s1);Stack s2(s1);return 0;
}


问题:

        如果不写拷贝构造函数,系统会自动生成吗?

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}private:int _year;int _month;int _day;
};typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){cout << "Stack()" << endl;_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}
private:DataType* _array;int _capacity;int _size;
};int main()
{Date d1(2025, 1, 1);Date d2 = d1;Stack st1;Stack st2(st1);return 0;
}


特征3:

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

我们不写,编译默认生成的拷贝构造,跟之前的构造函数特性不一样

1、内置类型, 值拷贝

 2、自定义的类型,调用他的拷贝

 总结:Date不需要我们实现拷贝构造,默认生成就可以用

 Stack需要我们自己实现深拷贝的拷贝构造,默认生成会出问题


MyQueue对于默认生成的几个函数非常受用,人生赢家

#include<iostream>
using namespace std;typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){cout << "Stack()" << endl;_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}Stack(const Stack& s){cout << "Stack(Stack& s)" << endl;// 深拷贝_array = (DataType*)malloc(sizeof(DataType) * s._capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}memcpy(_array, s._array, sizeof(DataType) * s._size);_size = s._size;_capacity = s._capacity;}void Push(DataType data){_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;free(_array);_array = nullptr;_size = _capacity = 0;}
private:DataType* _array;int _capacity;int _size;
};class MyQueue
{
private:Stack _pushst;Stack _popst;
};int main()
{MyQueue mq1;MyQueue mq2 = mq1;return 0;
}

注意:

	Date(const Date& d){//一般情况都加上constcout << "Date(Date& d)" << endl;_year = d._year;_month = d._month;_day = d._day;}Date(const Date& d){cout << "Date(Date& d)" << endl;//若不小心写反了,很容易忽视这里的错误d._year = _year ;d._month = _month;d._day = _day;}

五、运算符重载

    问题1:

          日期对象如何比较大小?能否用 < .

简单的代码实现:

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}//private:int _year;int _month;int _day;
};bool DateLess (const Date& x1,const Date& x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day){return true;}else{return false;}
}int main()
{Date d1(2025, 1, 1);Date d2(2025, 1, 11);cout<<DateLess(d1, d2)<<endl;return 0;
}


#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}//private:int _year;int _month;int _day;
};bool operator<(const Date& x1, const Date& x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day){return true;}else{return false;}
}int main()
{Date d1(2025, 1, 1);Date d2(2025, 1, 11);cout << (d1 < d2) << endl;cout << (operator<(d1, d2)) << endl;return 0;
}

写为成员函数:

// d1 < d2  
// d1.operator<(d2)
//操作数顺序不能随便换
bool operator<(const Date& d){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;}else{return false;}}

运算符复用

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}bool operator<(const Date& d){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;}else{return false;}}bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}// d1 <= d2bool operator<=(const Date& d){return *this < d || *this == d;}bool operator>(const Date& d){return !(*this <= d);}bool operator>=(const Date& d){return !(*this < d);}bool operator!=(const Date& d){return !(*this == d);}
}

实现日期+运算

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}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)会发现Date ret = d1 + 50;中d1也会改变,所以该写成+=Date& operator+=(int day){_day += day;while (_day > GetMonthDay(_year, _month)){// 月进位_day -= GetMonthDay(_year, _month);++_month;// 月满了if (_month == 13){++_year;_month = 1;}}return *this;}Date operator+(int day)   //出了作用域对象不在了不能用引用返回{Date tmp(*this);tmp += day;return tmp;//tmp._day += day;//while (tmp._day > GetMonthDay(tmp._year, tmp._month))//{//	// 月进位//	tmp._day -= GetMonthDay(tmp._year, tmp._month);//	++_month;//	// 月满了//	if (tmp._month == 13)//	{//		++tmp._year;//		tmp._month = 1;//	}//}//return tmp;}private:// 内置类型int _year;int _month;int _day;
};int main()
{Date d1(2023, 7, 21);Date d2(2022, 8, 21);Date ret = d1 + 50;ret.Print();d1.Print();return 0;
}


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了c++的基础知识。


http://www.ppmy.cn/server/164189.html

相关文章

C语言小项目——通讯录

功能介绍&#xff1a; 1.联系人信息&#xff1a;姓名年龄性别地址电话 2.通讯录中可以存放100个人的信息 3.功能&#xff1a; 1>增加联系人 2>删除指定联系人 3>查找指定联系人的信息 4>修改指定联系人的信息 5显示所有联系人的信息 6>排序&#xff08;名字&…

【愚公系列】《循序渐进Vue.js 3.x前端开发实践》038-使用CSS3创建动画(keyframes动画)

标题详情作者简介愚公搬代码头衔华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;CSDN商业化专家&#xff0c;阿里云专家博主&#xff0c;阿里云签约作者&#xff0c;腾讯云优秀博主&…

Python 梯度下降法(五):Adam Optimize

文章目录 Python 梯度下降法&#xff08;五&#xff09;&#xff1a;Adam Optimize一、数学原理1.1 介绍1.2 符号说明1.3 实现流程 二、代码实现2.1 函数代码2.2 总代码2.3 遇到的问题2.4 算法优化 三、优缺点3.1 优点3.2 缺点 四、相关链接 Python 梯度下降法&#xff08;五&a…

【Android】问deepseek存储访问

这些天deepseek爆火&#xff0c;我们来问问android问题看看&#xff0c;如果问android中的应用怎么访问外部存储&#xff0c;回答的很清楚&#xff0c;但是如果问的深入一些&#xff0c;比如Android中是怎么控制让应用不能读取其他应用的外部存储文件的&#xff0c;回答的比较抽…

Java实现.env文件读取敏感数据

文章目录 1.common-env-starter模块1.目录结构2.DotenvEnvironmentPostProcessor.java 在${xxx}解析之前执行&#xff0c;提前读取配置3.EnvProperties.java 这里的path只是为了代码提示4.EnvAutoConfiguration.java Env模块自动配置类5.spring.factories 自动配置和注册Enviro…

小程序电商运营内容真实性增强策略及开源链动2+1模式AI智能名片S2B2C商城系统源码的应用探索

摘要&#xff1a;随着互联网技术的不断发展&#xff0c;小程序电商已成为现代商业的重要组成部分。然而&#xff0c;如何在竞争激烈的市场中增强小程序内容的真实性&#xff0c;提高用户信任度&#xff0c;成为电商运营者面临的一大挑战。本文首先探讨了通过图片、视频等方式增…

C# Winform制作一个登录系统

using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;namespace 登录 {p…

DeepSeek本地版安装简易教程(windows)

第一步&#xff1a;下载 第二步&#xff1a;安装 先安装ollama&#xff0c;安装完毕保持ollama运行&#xff0c;设置ollama通过防火墙&#xff0c;再安装deepseek&#xff0c;7b代表下载的r1版本&#xff0c;版本越高消耗资源越大 第三步&#xff1a;开放windows防火墙 第四步…