20190117 C++ Primer学习笔记

本文深入探讨C++编程的关键概念,覆盖输入输出操作、变量类型、字符串处理、向量使用、函数定义、类设计及泛型算法应用。解析C++11新特性,如智能指针、范围for循环和右值引用,强调代码优化与异常处理技巧。
摘要由CSDN通过智能技术生成

Chapter 1 输入输出

  1. 键盘输入文件结束符:Ctrl+Z,再按Enter。
  2. 文件重定向。(^-^)V
`#include <iostream>
#include "Sales_item.h"
#include <ostream>
#include <fstream>
using namespace std;
int main()
{
	ifstream fin("book_in.txt");
	ofstream fout("book_out.txt");
	streambuf *cinback = cin.rdbuf(fin.rdbuf());
	streambuf *coutback = cout.rdbuf(fout.rdbuf());
	Sales_item total;
	if (cin >> total)
	{
		Sales_item trans;
		while (cin >> trans)
		{
			if (trans.isbn() == total.isbn())
				total += trans;
			else
			{
				cout << total << endl;
				total = trans;
			}
		}
		cout << total << endl;
	}
	else
		cout << "No data?" << endl;

	system("pause");
	return 0;
}``
 

Chapter 2 变量和基本类型

  1. int* p, p;//其中第一个*p是指针,第二个p是int
  2. 指向指针的引用 int *p = 0; int *&r = p;
  3. 和常量引用一样,指向常量的指针也没有规定其所指的对象必须是一个常量。只是不能通过该指针改变对象的值,但是可以通过其他的途径修改。
  4. 对于逻辑运算符&&,C++规定只有左侧运算对象为真时才会检查右侧运算对象的情况。

Chapter 3 字符串、向量和数组

  1. Vector v; 中vend = v.end(); *(vend - 1)才是最后一个数。因为b是尾后迭代器不存在,不能执行解引用或递增操作。
  2. auto vmid = vbeg + (vend - vbeg) / 2;
    要用这个算中间的迭代器,不能直接相加除2。因为迭代器没有加法运算。
autodecltype
P63: 一般忽略顶层const,底层const保留包括顶层const
P105:使用数组作为auto初始值时,得到指针使用数组作为auto初始值时,得到数组
  1. 要使用范围for来处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。为了避免数组被自动转成指针。P114

Chapter 4 表达式

  1. P147有全部运算符优先级。
  2. C++11:商一律向0取整。
  3. 取余不看右面数的正负,全看左边。
  4. 短路求值:逻辑与和逻辑或都先算左面,无法确定表达式值再算右面。
    如:index != s.size() && isspace(s[index]);
    首先检查是否到达string末尾,确保只有保证index合理才会用下标运算符去访问它。反之不行。
  5. 这样赋值是对的:int i = j = 0; 因为赋值运算符满足右结合律。
  6. 除非要用到递增(减)前的原始值,不用后置版本的递增(减)运算符。
    如:cout << *(v.begin()++) << endl; //输出当前值,并将v.begin()向前移动一个元素。
  7. 三目运算符使程序更简洁。
  8. 注意:逻辑与是&&;位与是&。其他类似。
  9. int x[10];//输出元素的个数。
    cout << sizeof(x) / sizeof(*x) << endl;
  10. sizeof§,p是一个指针,返回值是指针的大小永远都是4。
    若是sizeof(p)则看p是什么类型,返回值就是该内置类型的大小。

Chapter 5 语句

  1. try语句内声明的变量在块外无法访问,注意包括catch子句。

  2. 异常处理示例:
    在这里插入图片描述

  3. 连续输入两个vector:cin.clear();//输入一个之后加上这句话

  4. 交换两个同类型vector的内容:v1.swap(v2);

  5. 习题5.17

#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
using namespace std;
int main()
{
	vector<int> v1, v2;
	int data;
	while (cin >> data)
		v1.push_back(data);
	cin.clear();				//连续输入两个容器内容注意要有cin.clear();
	while (cin >> data)
		v2.push_back(data);			
	if (v1.size() > v2.size())
		v1.swap(v2);			//如果v1大于v2,交换两个容器内容
	/*for (auto &c : v1)
		cout << c << endl;
	for (auto &c : v2)
		cout << c << endl;*/
	else if (v1.size() == v2.size())
	{
		cout << "一样长不是前缀" << endl;
		system("pause");
		return 0;
	}
	auto vbeg1 = v1.begin(); 	//这几个定义要写在交换后面
	auto vend1 = v1.end() - 1;
	auto vbeg2 = v2.begin();
	for (; vbeg1 != v1.end(); ++vbeg1, ++vbeg2)
	{
		if (*vbeg1 != *vbeg2)
		{
			cout << "元素不一样不是前缀" << endl; break;
		}
		else if (vbeg1 == vend1)
			cout << "是前缀" << endl;
	}	
	system("pause");
	return 0;
}

Chapter 6 函数

  1. 形参列表中每个形参都是含有一个声明符的声明,即使两个形参的类型一样,也必须都写出来。 Void f3(int a, int b);
  2. 局部静态对象在程序执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁。
    若没有显示的初始值,执行值初始化,内置类型的静态局部变量初始化为0。
  3. 顶层const作用于对象本身,底层const则与指针和引用等复合类型的基本类型部分有关。初始化一般会忽略顶层const。
  4. 尽量使用常量引用(函数内部不需要改变参数时),不能把const对象,字面值或需要类型转换的对象传给普通引用。指针同理。
  5. 数组的首元素指针和尾后指针:begin(a); end(a);
  6. 数组引用形参:f(int (&arr)[10]); 其中(&arr)的括号必不可少。P195,196
    不能用传值的方式传递数组(f(const int a[10])是错的),只能用指针或引用:
    在这里插入图片描述
  7. 不要返回局部对象的引用或指针,函数结束的时候临时对象的空间就被释放。
  8. 数组初始化vector:int a[]={…}; vector v = (a, a+sizeof(a) /sizeof(*a));
  9. 各种函数返回复杂类型的写法用到再找书吧。。。
  10. 不在局部作用域中声明函数。
  11. 一旦某个形参被赋予了默认值,它后面所有的形参必须都有默认值。
  12. 函数返回类型前加上关键字inline,变成内联函数。P213优化。

Chapter 7 类

  1. 访问说明符指定了接下来的成员的访问级别,有效范围直到出现下一个说明符或到类的结尾为止。
    
  2. struct默认public,class默认private。
  3. 如果我们已经提供了一个构造函数,那么编译器不会自动生成默认的构造函数。如果我们的类需要默认的构造函数,必须把它显式的声明出来。
    如:Screen() = default;
  4. 定义在类内部的成员函数是自动inline的。
  5. 任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量。
  6. 一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是一个常量引用,不可修改。
  7. const Screen &display(std::ostream &os) const; //后面的const是修改隐式this指针的类型
    
    一个类就是一个作用域,在类外部定义成员函数时必须提供类名和函数名。
  8. 如果成员使用了外层作用域的某个名字,而改名字代表一种类型,则类不能在之后再定义该名字。
  9. 成员定义中的普通块作用域的名字查找:
  1. 成员函数内
  2. 类内
  3. 成员函数定义之前的作用域内
  1. 成员初始化顺序与它们在类定义中的出现顺序一致,构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。
  2. 算术类型、引用和指针都属于字面值类型。
  3. 类的静态成员:该成员只需与类的本身有关,而不是与类的对象有关,加上static关键字即可声明,其不与任何实例化对象绑定,但是我们仍然可以使用类作用域运算符访问静态成员。
  4. static声明在内部。在外部定义时,不加static。类似与一个全局变量,其初始值必须是常量表达式。
  5. 静态成员独立于任何对象,其类型可以是它所属的类类型。而非静态成员只能声明为其类的指针或引用
    有的题要把类型写大点。。。比如int换成long就好了

Chapter 8 IO类

  1. 由于流可能处于错误状态,因此代码通常应该在使用一个流之前检查它是否处于良好状态。While(cin>>word){//ok}
  2. 如果程序崩溃,输出缓冲区不会被刷新。
  3. 对一个已经打开的文件流调用open会失败。为了将文件流关联到另一个文件,必须首先关闭已经关联的文件。一旦文件成功关闭,我们可以打开新的文件。

Chapter 9 顺序容器概述

  1. 选择容器的规则:除非有很好的理由选择其他容器,否则使用vector。P293
  2. v.begin();的类型是iterator。 vector v :: iterator v.begin();
  3. 容器的拷贝初始化:拷贝整个容器要求容器类型和元素类型都必须相同;
    拷贝元素时只要能将要拷贝的元素转换为要初始化的容器的元素类型即可。
    元素拷贝:vector words(v.begin(), v.end());
  4. 不能对内置数组类型进行拷贝或对象赋值操作,但array无限制。
  5. 顺序容器的assign允许从一个不同但相容的类型赋值,或从容器的一个子序列赋值。
  6. 关系运算符左右两边只能是相同类型的容器,且必须保存相同类型的元素。
  7. 逆序输出可以用push_front()实现。
  8. insert函数将元素插入到迭代器所指的位置之前。
  9. 通过使用insert的返回值,可以在容器一个特定位置反复插入元素。c++11
  10. emplace构造,push_back、insert拷贝。P308
  11. front,back,at和下标返回的都是引用。其中at和下标类似,但下标是否越界需要自己判断,v.at(n)越界会抛出异常。
  12. 这一章的warning里写了好多会使迭代器、引用、指针失效的操作。例如:向一个vector、string或deque插入元素会使所有指向容器的迭代器、引用和指针失效。P315专门讲
  13. 习题9.26
#include<iostream>
#include<vector>
#include<list>
using namespace std;

int main(int argc, char**argv)
{
	int ia[] = {0, 1, 1, 2, 2, 3, 5, 8, 13, 21, 55, 89};
	vector<int> v(ia, ia + 12); 	//拷贝初始化
	list<int> l(ia, ia + 12);
	list<int>::iterator lbeg = l.begin();
	while (lbeg != l.end()) 		//用for不行,for(){}各自增一次就乱了
	{
		if (*lbeg % 2)//删除奇数
			lbeg = l.erase(lbeg); 	//返回的是被删除数下一个数的迭代器,相当于自增
		else
			lbeg++;
	}
	vector<int>::iterator vbeg = v.begin();
	while(vbeg != v.end())
	{
		if (!(*vbeg % 2))//删除偶数
			vbeg = v.erase(vbeg);
		else
			vbeg++;
	}
	for (auto c : l)
		cout << c << endl;
	for (auto c : v)
		cout << c << endl;
	system("pause");
}
  1. 习题9.28
#include<iostream>
#include<vector>
#include<string>
#include<forward_list>
using namespace std;

void myfun(forward_list<string> &f, string a, string b)//传引用才会改变原始值!!!
{
	int flag = 0;
	auto prev = f.before_begin();
	auto curr = f.begin();
	while ( curr != f.end())
	{
		if (*curr == a)
		{
			f.insert_after(curr, b); flag = 1;
		}	
		prev = curr;
		curr++;
	}
	if (flag == 0)
		f.insert_after(prev, b);	//这里要用prev
}

int main(int argc, char**argv)
{
	forward_list<string> f = { "abc", "def", "gd", "hijk" };
	string a = "gd";
	string b = "lalala";
	myfun(f, a, b);
	for (auto c : f)
		cout << c << endl;
	system("pause");
}
  1. 如果需要插入或删除容器中的元素,不要提前保存end()返回的迭代器,每次循环都重新调用,否则可能会失效导致无限循环。
  2. v.reserve(n); 分配至少能容纳n个元素的内存空间。
  3. v.capacity(); 不重新分配内存空间的话,v可以保存多少元素。
  4. capacity和size不同,一个是最多能保存多少,一个是已经保存的数目。capacity大于等于size。
  5. P321、323有许多构造string和修改string的操作。
  6. 书上强调了几次以空格结尾的字符?有时候程序不对加上空格就好了?
    Chapter 10 泛型算法
  7. 可以通过调用accumulate将vector中所有的string元素连起来:string sum = accumulate(v.begin(), v.end(), string(""));
  8. equal可以比较两个不同类型容器中的元素,元素类型也可以不同,只要能用==比较即可。
  9. 用一个单一迭代器表示第二个序列的算法都假定第二个序列至少与第一个一样长,保证这个是程序员的责任。
  10. 消除重复单词:先用sort排序使得重复的元素相邻出现,再用unique重排vector,使得不重复的元素出现在vector的开始部分,再用erase删除后面重复的部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值