【cpp】深蓝学院 c++课程 基于课后作业的学习复习汇总 其1 1-6节

写在前面

N年前上来深蓝学院cpp的课,老师是大牛但却有讲的特别细致,感觉对于入门的我有很大帮助。当时作为初学者有很多不理解的地方,现在工作了几年回头去看理解深刻了一些,将会在学习行业算法的时候,复习一下课程内容。

不会去罗列课程知识点,老师讲的太细致了,可以作为工作书遇到回来再查看。其实问CHATGPT也很方便了,但问之前也需要有一个知识框架。

同时网上书籍也很多,这里就计划基于每一章节的作业区复习整理相关的知识,即作业使用到了知识点才复习,同时与实际使用时尚未见过的章节和知识点会优先级排后。

知识点是我写代码过程中或修改时候时候想到要使用的,对于记忆不清的内容在最后做了查询的记录

第一二三章合并作业

  • 设计自动出加减法和判断的题目
  • 知识点:无特别

效果

在这里插入图片描述

qkf代码

#include <cstdlib>
#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <ctime>
#include <chrono> // 包含用于时间计算的头文件
using namespace std;
using namespace std::chrono; class Calculator
{
public:Calculator() = default;~Calculator() = default;void run(const int &count){int counter = 0;int count_t = count;duration<double> total_time{0}; while (count_t--){auto start_time = high_resolution_clock::now(); counter += static_cast<int>(computing());auto end_time = high_resolution_clock::now();total_time += duration_cast<duration<double>>(end_time - start_time);}cout << "In total " << counter << " out of " << count << " are correct." << endl;cout << "Total time spent: " << total_time.count() << " seconds." << endl; }private:int stringToNumber(const string &input){int res = 0;int size = input.length(); // no \0for (const auto &x : input){res += (x - '0') * pow(10, size - 1);--size;}return res;}bool computing(){int a = rand() % 100 + 1;int b = rand() % 100 + 1;printf("Please enter the result of %d + %d: \n", a, b);string c = "";while (1){cin >> c;bool isNumber = true;for (const auto &x : c){if (x > '9' || x < '0'){printf("Please enter an integer, not a character.");isNumber = false;break;}}if (isNumber){break;}}int temp = stringToNumber(c);printf("The correct answer is %d and yours is %d.\n", a + b, temp);return (temp == a + b);}
};int main()
{srand(static_cast<unsigned int>(time(nullptr)));Calculator caltest1;caltest1.run(10);cout << "Press 'q' to quit: ";while (cin.get() != 'q');return 0;
}

扩展记录

rand随机种子

 srand(static_cast<unsigned int>(time(nullptr)));

输入流及其回车控制问题

见链接挺好

char 和string区别终止符问题

QKF:char 和string的结尾 哪一个有用到 \0

GPT:在 C++ 中,字符串的结尾通常由空字符 \0(空字符,ASCII值为0)表示。这被称为字符串的null终止符。这个字符告诉程序字符串的结束位置。在处理字符串的函数中,很多都会使用这个 null 终止符来确定字符串的末尾。

对于字符数组(char 数组),如果你手动创建一个字符数组来存储字符串,你需要确保在字符串的末尾添加 null 终止符。例如:

cpp
char myString[10];
myString[0] = ‘H’;
myString[1] = ‘e’;
myString[2] = ‘l’;
myString[3] = ‘l’;
myString[4] = ‘o’;
myString[5] = ‘\0’; // 添加 null 终止符表示字符串的结束
在这个例子中,myString 数组的前5个元素包含了字符串 “Hello” 的字符,第6个元素是 null 终止符。

相反,对于 std::string 类型,C++ 标准库会自动处理字符串的 null 终止符。你只需要在字符串的初始化时赋值即可:

cpp
std::string myString = “Hello”; // C++ 标准库会自动在末尾添加 null 终止符
在 std::string 中,你无需手动添加 null 终止符,它会自动处理字符串的大小和 null 终止符的添加。

总体而言,C++ 标准库的 std::string 类型对于字符串的处理更加方便,避免了手动添加 null 终止符的烦琐工作,并提供了更多的字符串操作函数。如果你使用字符数组,要确保手动添加 null 终止符,以确保字符串处理函数正确识别字符串的结束。

第四五六合并作业章

  • 题目 设计一个大数加减法
  • 知识点:构造、运算符重载、友元、文件流、main函数入口参数传递、迭代器(反向)

QKF代码


#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>class LongInteger
{
private:std::vector<int> digits_;bool isNegative_;         // 标记整数是否为负public:LongInteger(const std::string &number = "0") : isNegative_(false){bool isNonZero = false; // 标记是否已经遇到非零数字for (char digit : number){if (digit == '+' || digit == '-'){isNegative_ = (digit == '-');}else if (digit != '0' || isNonZero){isNonZero = true;digits_.push_back(digit - '0'); }}if (digits_.empty()) // 应对全是0的情况{digits_.push_back(0);}}// 加法重载LongInteger operator+(const LongInteger &other) const{if (isNegative_ == other.isNegative_){ // 同号return addDigits(other);}else if (isGreaterAbs(other)){ // 异号且当前数绝对值大return subtractDigits(other);}else{ // 异号且另一个数绝对值大return other.subtractDigits(*this);}}// 重载输出friend std::ostream &operator<<(std::ostream &os, const LongInteger &num){if (num.isNegative_ && !num.digits_.empty())os << '-';if (num.digits_.empty())os << '0';else{for (int digit : num.digits_){os << digit;}}return os;}private:// 加法LongInteger addDigits(const LongInteger &other) const{std::string sum;int carry = 0;auto it1 = digits_.rbegin(), it2 = other.digits_.rbegin();while (it1 != digits_.rend() || it2 != other.digits_.rend() || carry)//反向迭代器 ++为--{int val1 = it1 != digits_.rend() ? *it1++ : 0;int val2 = it2 != other.digits_.rend() ? *it2++ : 0;// 取最大长度不够补0  int total = val1 + val2 + carry;carry = total / 10;sum.push_back('0' + total % 10);// to_string 也可以}std::reverse(sum.begin(), sum.end());return LongInteger(isNegative_ && !sum.empty() ? "-" + sum : sum);// 排除 -0的情况, 同时结果还是用LongInteger表示//其实vector也可以,这里为了使用友元和重载os}// 减法,假设当前数字绝对值大于另一个数字LongInteger subtractDigits(const LongInteger &other) const{std::string difference;int borrow = 0;auto it1 = digits_.rbegin(), it2 = other.digits_.rbegin();while (it1 != digits_.rend() || it2 != other.digits_.rend()){int val1 = it1 != digits_.rend() ? *it1++ : 0;int val2 = it2 != other.digits_.rend() ? *it2++ + borrow : borrow;//把借位放在减数上if (val1 < val2){val1 += 10;borrow = 1;}else{borrow = 0;}difference.push_back('0' + val1 - val2);}std::reverse(difference.begin(), difference.end());return LongInteger(isNegative_ && !difference.empty() ? "-" + difference : difference);// 减法正负号可以由两数绝对值大小大的数字来界定,亦可以使用大减小和小减大分类讨论,这里使用第一种方式}// 比较绝对值大小bool isGreaterAbs(const LongInteger &other) const{if (digits_.size() != other.digits_.size())// 这时候以及除前导零了可以直接用size来比较{return digits_.size() > other.digits_.size();}for (size_t i = 0; i < digits_.size(); ++i){if (digits_[i] != other.digits_[i]){return digits_[i] > other.digits_[i];}}return false; }
};int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " <filename>" << std::endl;return 1;}std::ifstream file(argv[1]);std::string line1, line2;if (std::getline(file, line1) && std::getline(file, line2)){LongInteger num1(line1), num2(line2);std::cout << num1 + num2 << std::endl;}else{std::cerr << "Error reading file." << std::endl;}return 0;
}

效果

在这里插入图片描述

拓展记录

size_t

size_t 是专门用来表示对象大小和数组索引的类型,它的最大值应该足够大以容纳系统中可能存在的最大对象或数组

main 入口参数

第一个是参数数量 ,默认按顺序是有程序名 和 各种参数,各种参数在上图红框中体现,存储在 char *argv[] 中,就好像传入数组,给个地址头给一个参数size

错误输出

QKF:cout cerr 区别
缓冲区:

cout 对象通常是经过缓冲的,这意味着它的输出可能会先存储在内存缓冲区中,直到遇到换行符 \n 或显式地刷新缓冲区时才会被输出到终端。这种缓冲行为有助于提高输出效率,但也可能导致在程序崩溃或异常退出时丢失输出内容。
cerr 对象通常是不经过缓冲的,它的输出会立即被发送到终端,这样可以确保及时显示出错信息,即使在程序崩溃或异常退出时也能够看到相应的错误信息。
标准错误输出:

cerr 通常用于向标准错误流输出错误信息。与 cout 不同,cerr 的输出通常不会被重定向到文件或管道中,而是直接显示在终端上,这使得它非常适合用于输出与程序执行相关的重要错误信息,如运行时错误、异常情况等。
cout 用于标准输出流,通常用于输出一般性的信息、警告或调试信息。

反向迭代器

for (auto it = num.digits.rbegin(); it != num.digits.rend(); ++it)//反向迭代 ++实际为减法

列表初始化

提高了运行速度比起在函数体内赋值

 LongInteger(const std::string &number = "0") : isNegative_(false)

友元函数

使用了友元函数,因为使用重载的<<时候,在外部访问了类的private成员。而+号运算法没有

    LongInteger operator+(const LongInteger &other) const{if (isNegative_ == other.isNegative_){ // 同号return addDigits(other);}else if (isGreaterAbs(other)){ // 异号且当前数绝对值大return subtractDigits(other);}else{ // 异号且另一个数绝对值大return other.subtractDigits(*this);}}// 重载输出friend std::ostream &operator<<(std::ostream &os, const LongInteger &num){if (num.isNegative_ && !num.digits_.empty())os << '-';if (num.digits_.empty())// 至少给个0,当处理过的数据为空os << '0';else{for (int digit : num.digits_){os << digit;}}return os;}

QKF:为什么成员函数 +在被使用的时候不是 num1.+nums2 而是可以直接写成 num1+num2
GPT:在 C++ 中,当你使用 obj1 + obj2 形式时,编译器会将其转换为 obj1.operator+(obj2) 的形式,其中 operator+ 是一个成员函数。这种转换是因为 C++ 支持一种特殊的语法,称为运算符重载(operator overloading),允许你重载类中的某些运算符,使得它们能够操作类的对象。

因此,当你写 num1 + num2 时,实际上是在调用 num1 对象的 operator+ 成员函数,并将 num2 作为参数传递给该函数。这种语法使得代码更加清晰和易读,因为它反映了你想要执行的操作,而不需要显式地写成 `num1.operator+(num2)

额外挺好的解释

<<的重载和链式传递

这一块书写的时候我借鉴了资料,但上面 <<的重载我理解的不是很透彻,为什么一定要有形式参数传入且同时返回,返回我能理解是为了链式传递,但是为什么不能局部一个ostream,GPT的回答ostream是一个虚基类无法实例化,我可以理解,这个回答。 GPT推荐使用ostringstream 类型来声名局部变量

#include <sstream>friend std::ostream& operator<<(const LongInteger& num) {std::ostringstream os;if (num.isNegative_ && !num.digits_.empty())os << '-';if (num.digits_.empty()) // 至少给个0,当处理过的数据为空os << '0';else {for (int digit : num.digits_) {os << digit;}}return os;
}

在看到输出运算符重载的时候 不能太理解为什么+可以定义为友元但是<<不可以,询问了gpt
QKF:为什么不能直接把<< 定义为成员函数 是因为返回值类型的原因嘛
GPT: << 定义为成员函数的确是一种常见的做法,但它可能会导致一些问题。在C++中,重载 << 运算符作为成员函数,需要将左操作数(被输出的对象)作为调用者,而右操作数(输出流)作为参数。然而,这种方式限制了左操作数的类型,因为成员函数必须属于该类型或者可以隐式地转换为该类型。

这样做可能会导致两个问题:

修改原始类: 如果你想要将 << 定义为成员函数,你需要修改原始类的定义,将它包含在你自己的类中。这样一来,如果你没有控制这个类的定义,就无法添加这样的成员函数。

左操作数的类型限制: 如果 << 被定义为成员函数,它只能用于该类的对象,并且只能接受右操作数作为参数。这样一来,如果你想要支持多种类型的左操作数(例如自定义类型和标准类型),你就需要在每个类型中定义对应的 << 成员函数,这会增加代码的冗余度。

因此,将 << 定义为非成员函数,而是作为友元函数或全局函数,通常更加灵活和通用。这种方式可以支持更多类型的左操作数,并且不需要修改原始类的定义。此外,非成员函数的形式还更符合面向对象编程的原则,因为输出操作通常不需要直接访问类的私有成员。

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

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

相关文章

【详识JAVA语言】猜数字游戏

游戏规则: 系统自动生成一个随机整数(1-100), 然后由用户输入一个猜测的数字. 如果输入的数字比该随机数小, 提示 "低 了", 如果输入的数字比该随机数大, 提示 "高了" , 如果输入的数字和随机数相等, 则提示 "猜对了" . 参考代码 import java.…

当你的游览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么

当你的游览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么 1.负责域名解析的 DNS 服务 DNS&#xff08;Domain Name System&#xff09;服务是和 HTTP 协议一样位于应用层的 协议。它提供域名到 IP 地址之间的解析服务。 计算机既可以被赋予 IP 地址&#xf…

Ubuntu系统使用Docker搭建Jupyter Notebook并实现无公网ip远程连接

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpolar内网穿透…

【前端素材】推荐优质后台管理系统DAdmin平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理网站、应用程序或系统的管理界面&#xff0c;通常由管理员和工作人员使用。它提供了访问和控制网站或应用程序后台功能的工具和界面&#xff0c;使其能够管理用户、内容、数据和其他各种功能。 2、功能需求 后台管理系…

Python:关于数据服务中的Web API的设计

搭建类似joinquant、tushare类似的私有数据服务应用&#xff0c;有以下一些点需要注意&#xff1a; 需要说明的是&#xff0c;这里讨论的是web api前后端&#xff0c;当然还有其它方案&#xff0c;thrift&#xff0c;grpc等。因为要考虑到一鱼两吃&#xff0c;本文只探讨web ap…

利用Quartz实现复杂的任务调度

第一章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;任务调度&#xff0c;简而言之&#xff0c;就是按照预定计划自动执行任务的过程。不管是数据库备份、报表生成还是发送定时邮件&#xff0c;它们都需要一个可靠的任务调度系统来保证按时完成。 那么&#xff0…

用node或者vscode开启一个简单的本地server服务器,加载html网页

使用Live Server 想要加载本地html页面可以快速能让它在你本地浏览器中打开&#xff0c;可以有好多种方式&#xff0c;如果你有使用vscode&#xff0c;可以安装一个插件&#xff1a;Live Server&#xff0c;然后直接在vscode中直接右键就可以开启这个服务&#xff1a; 安装好之…

几个原创文章自动生成软件推荐

在数字化时代&#xff0c;原创文章的重要性越来越被重视&#xff0c;但是频繁的写作工作却也耗费大量时间和精力。原创文章自动生成软件能够帮助大家快速生成各种类型的原创文章&#xff0c;节省时间和精力。本文将介绍6款不同的原创文章自动生成软件&#xff0c;并介绍一款知名…

02|JVM内存模型

1. JVM整体结构及内存模型 1.1 类装载子系统 负责加载字节码文件并将其转换为可以执行的Java类。类加载器子系统包括三个主要的类加载器&#xff1a;Bootstrap ClassLoader&#xff08;引导类加载器&#xff09;、Extension ClassLoader&#xff08;扩展类加载器&#xff09;和…

linux nasm汇编中调用printf不报错,但调用scanf报错。抛出了分段错误(核心转储)

当我写了如下汇编时 ; nasm -f elf64 -g -F dwarf charsin.asm ; gcc charsin.o -no-pie -o charsin ; ld -o eatclib eatclib.o ; gdb eatclib[SECTION .data]SPrompt db Enter string data, followed by Enter: ,0IPrompt db Enter an integer value, followed by Enter: ,1…

人工智能应用工程师职业技能提升如何考取,需要具备怎样的技能?

人工智能应用工程师是能够利用人工智能相关技术进行应用研发&#xff0c;并开展各类工作的从业人员统称。 人工智能应用工程师考试是对人工智能领域从业者的全面认证&#xff0c;共分为初级、中级、高级三个等级&#xff0c;分别对应了人工智能应用工程师未来发展的三个大致方向…

本地写的Bash脚本,Linux端运行报错:/bin/bash^M: bad interpreter: No such file or directory

背景 在本地写了个Bash Shell脚本&#xff0c;但上传到Linux端后加完权限执行时报错&#xff1a; &#xff08;脚本名&#xff1a;script.sh&#xff09; -bash: ./script.sh: /bin/bash^M: bad interpreter: No such file or directory 分析 这个错误通常是由于脚本文件的行…