C++Primer 第一章
C++概述
程序的组成
- 数据的集合
- 算法的集合
面向对象编程:我们通过一组数据抽象来建立问题的模型,简单来说就是把一些数据封装起来,就是我们类
面向过程编程:一个问题可直接由一组算法来建立模型
第一章 开始
1.1 问题的解决
这里有一个简单的例子来让我们看看如何设计编程思路,有的朋友可能想要把这个例子给手写出来,这里我也把自己的写法附在下面,供大家参考,写的比较简单,主要是把思路理顺.
我是这样写的,我们把文件中销售记录给读出来,后面经过排序格式化输入到另一个文件中,这个版本有很大的缺陷,例子说了是两个星期进行一次,这里我没有实现,我的排序是STL库默认的升序,这里大家可以使用降序.我这里都是使用的基本的类型大家可以进行封装,例如把书名和销售量封在一起,看搭建的思路吧.
//某个书店将每本售出图书的书名和出版社输入到一个文件中,这些信息以书售出的时间顺序输入.
//每两周店主将手工计算每本书的销售量以及每个出版社的销售量报表以出版社名称的字母顺序排列以使下订单
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <unordered_map>
#include <cstring>
#define SPE " "
#define LEN strlen(SPE)
class BookSales
{
public:
BookSales(const std::string& src, const std::string& dest)
:_file_name(src)
,_result_name(dest)
{}
public:
void run()
{
readFile();
calculateSales();
sortSales();
result();
}
private:
// 1. 读销售记录
void readFile()
{
// 我们的格式应该是 书名 出版社 年-月-日\r\n
std::ifstream read_file(_file_name);
if(read_file.is_open() == false)
{
std::cerr << "文件不存在" <<std::endl;
return;
}
std::string str;
while(std::getline(read_file, str))
{
_sales_records.push_back(std::move(str));
}
}
// 2. 根据书名和出版社计算销售量
void calculateSales()
{
// 这个我们按照
// 书名 出版社 销售量来
for(auto& e: _sales_records)
{
std::string name;
std::string publishing_house;
std::size_t pos1 = e.find(SPE, 0);
if(pos1 == std::string::npos)
continue;
std::size_t pos2 = e.find(SPE, pos1+LEN);
if(pos2 == std::string::npos)
continue;
name = e.substr(0, pos1);
publishing_house = e.substr(pos1+LEN, pos2-pos1-LEN);
std::unordered_map<std::string, int>& m = _map[publishing_house];
m[name]++;
}
}
// 3. 以出版社名称对书名进行排序
void sortSales()
{
// 注意,我们使用是map,底层是二叉树,已经排好序了,我不知道要不要对销量经行排序,不过
// 我感觉排好序更加好一线,这里我们来直接排序一下,这里我们继续使用一个map,只不过这里我们让销量做K
for(auto& p:_map)
{
auto& it = _result[p.first];
auto iter = p.second.begin();
while(iter != p.second.end())
{
it.insert(std::make_pair(iter->second, iter->first));// 销量:书名
iter++;
}
}
}
// 4 . 输出结果
void result()
{
// 这里是输出结果,我们把输出的记过放在一个文件中
std::ofstream outfile(_result_name);
if(outfile.is_open() == false)
{
std::cerr << "打开文件失败" << std::endl;
return;
}
for(auto& p: _result)
{
std::string str = p.first;
str += SPE;
auto iter = p.second.begin();
while(iter != p.second.end())
{
std::string s = str;
s += iter->second;
s += SPE;
s += std::to_string(iter->first);
s += "\r\n";
outfile << std::move(s);
iter++;
}
}
}
private:
std::string _file_name; // 这个是我们保存销售文件的名字
std::string _result_name;
std::vector<std::string> _sales_records; // 每一条销售记录
std::map<std::string, std::unordered_map<std::string, int>> _map; // 这个是计算销量
std::map<std::string, std::multimap<int, std::string>> _result; // 这个是计算销量
};
1.2 C++程序
说一下声明和定义,这里是个人理解.
- 声明 不开辟空间
- 定义 开辟空间
extern a; // 声明
int b; // 个人感觉是定义,开辟空间了
int c = 10; // 定义并初始化
1.3 预处理器指示符
所谓的预处理器指示符就是我们的#,他的作用有下面两个作用
- 引用头文件
- 宏定义
来解释一下在引用头文件究竟是如何做到的,这里很简单,但编译器看到这是头文件的引用时,就会在路径中寻找这个文件,然后把这个文件的内容直接添加到我们的代码中,这里到程序是如何运行那里非常清楚.至于宏定义我就不谈了,之前写过博客.
我们一直谈防止头文件被引用?什么意思?我们好像从来没有遇到过这个情况?这里说一下,由于大家可能会把函数的定义写道hpp文件中,那么此时我们多次重复引用这个hpp文件就会出现问题,大家看一下.
这就是我们为何要防止头文件被重复引用的问题,至于如何解决,可以看书或者搜一下,我们不谈.
下面来说如何在命令行中如何定义一个宏,这个还是有点用的,这里很简单,选项是D.
#include <iostream>
int main()
{
#ifdef DEBUG
std::cout <<"宏已经被定义" << std::endl;
#else
std::cout <<"宏没有被定义" << std::endl;
#endif
return 0;
}
说下有作用的两个宏,一个是当前宏所在的函数,一个是得到该程序源文件的名字
注意,下面我们要知道assert是一个宏,注意是一个宏,不是函数.它的用法一般是为了检测参数的合法性,不过所谓的合法性也是存在限制的.
我们知道C++是支持C语言的,那么C语言的头文件如何在C++中引用,这里有两个方法
- 直接使用,方法不变
- 把.h去掉,然后前面加上一个c
下面举一个例子,他的作用是一样的.
#include <stdio.h>
#include <cstdio>
书上说这两个方式效果不一样?这里我感觉有点疑惑,可能是我没有看太懂,我们std命名空间里面好像是没有assert的,不过我把段话的意思给大家分析一下,就是我们使用C语言风格的方式,那么我们使用没有问题,如果是G++风格的,一些东西可能会被封装到std中,看个人使用遇到的情况吧.
1.4 注释
C++的注释和C语言是一样的,我们要善于使用注释.
1.5 输入/输出初步
C++除了格式化输入和输出之外也支持自己的输入和输出,而且这个更加的方便,下面直接下例子.
#include <iostream>
int main()
{
int a = 0;
std::cin >> a;
std::cout << a;
std::cout << std::endl; // endl 就是换行符
std::cerr << a;
std::cout << std::endl; // endl 就是换行符
return 0;
}
说的其他的,首先标准输入在输入字符串的时候遇到空格会结束
这里我们推荐使用getline函数,先来看现象.
int main()
{
std::string str;
//std::cin >> str;
std::getline(std::cin, str);
std::cout << str << std::endl;
return 0;
}
1.5.1 文件输入和输出
我们可以把一个文件也实例化出一个对象,这样我们也开始可以使用>>或者<<来进行输入和输出的,这是只测试输出,毕竟后面会专门谈到
#include <iostream>
#include <string>
#include <fstream>
int main()
{
std::string file_name = "file.txt";
std::ofstream out_file(file_name); // 打开一个文件供输出
if(out_file.is_open() == false)
{
std::cerr << "打开文件失败" << std::endl;
return 0;
}
std::string str = "为了测试";
for(int i = 0; i < 3; i++)
out_file << str;
return 0;
}