C++ 随记4 (仍然是杂项)

1、size_type类型

这属于是基础没打好了。

size_type是由stringvector类型定义的类型,用于保存任意stringvector对象的长度。

  • 切记,size_typeunsigned的。
  • size_type的长度,会随机器改变而改变。

更详细地将请移步这篇博客

2、for_each

for_each(InputIterator first, InputIterator last, Function fn);

注意:for_each是对firstlast里的每个【元素】,调用函数fn。所以传入fn的参数并不一定是前面的迭代器或指针类型。

3、

左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。

左值持久,右值短暂


4、

拷贝构造函数第一个参数必是该类型的一个左值引用,任何额外的参数都必须有默认实参。


5、noexpect:承诺一个函数不抛出任何异常

  • noexcept写在参数列表和初始化列表之间。
  • 生命和定义都要加noexcept在这里插入图片描述

6、一个普通的只有类型名修饰的参数即可以接受左值,也可以接受右值。

X& X::operator=(X x)
{}

X& X::operator=(X&& x)
{}

也就是说,如果类X有这样两个重载的赋值运算符的话,用右值做赋值运算符右侧的运算单元就会报错。因为两个函数都能接收右值,产生二义性。

7、只有重载的函数调用运算符operator()能有默认实参,其他重载运算符不能有默认实参。


8、

重载操作符不在保证操作数的求值顺序,所以一般不重载&& || ,


9、"hello" == "world"

像这样直接比较两个char*类型的C风格字符串的话,比较的是指针

10、重载输入运算符要处理输入失败

在这里插入图片描述

X& operator>>(istream& is, X& x)
{
	is >> 输入巴拉巴拉.....
	if (!is)		这个检查是一次性检查,没有逐个检查每个读取操作
		x = X();	输入失败,赋予对象默认状态
	else		
		某些成员需要由刚刚出入的数据计算出来,则在确保输入正常的情况下进行
	return is;
}

发生输入错误可能是如下原因:

  • 输入的数据类型与代码要求的不符
  • 读取到达文件末尾,或遇到其他错误

不过输入也是有强转的,当对一个int输入一个double时,不会出错,int只保留的输入的整数部分。

标示错误

在这里插入图片描述

11、算术运算符 和 复合赋值运算符

在这里插入图片描述
因为使用复合赋值运算符来实现算数运算符是很方便的。如下所示:

Sale_data operator+(const Sale_data& s1, const Sale_data& s2)
{
	Sale_data sum = s1;
	sum += s2;		直接使用复合赋值运算符就行了,而不用逐成员相加
	return sum;
}

12、定义类的加法和减法运算符,实现指针的算术运算

在定义的时候要注意与内置版本保持一致,也就是返回一个结果的拷贝,而不是引用,因为进行指针算术运算我们是希望通过计算后的指针去访问值。所以在实现的过程中我们也不应改变源对象,而是拷贝一个副本,改变副本的值。如下面代码所示:

类的定义就不写了,意会吧,假设类名是X,他有个成员是指针

X operator+(int n)
{
	X x = *this;
	x.pointer += n;
	return x;
}

X operator-(int n)
{
	X x = *this;
	x.pointor -= n;
	return x;
}

13、replace_if函数

replace_if是一个标准库提供的泛型算法。

void replace_if(ForwardIterator first, ForwardIterator last,
                   							UnaryPredicate pred, const T& new_value );

前两个参数指定范围。

第三个参数是一个谓词:接受范围内的元素作为参数,并返回bool值,若为true则用第四个参数替换他,反之不替换。

14、count_if函数

count_if是一个标准库他一共的泛型算法。

typename iterator_traits<InputIterator>::difference_type
				count_if(InputIterator first, InputIterator last, UnaryPredicate pred);

上面那个是返回值,总的来说是difference_type类型。
前两个参数指定范围。
第三个参数是一个谓词,返回值是bool值,该函数能计算出范围内使谓词返回true得元素数量。

该函数在这个题里面有妙用:
在这里插入图片描述


15、find_if函数

这也是标准库的算法。

InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);

前两个参数指明范围。
第三个参数是个谓词,返回bool值。
该函数最终返回指向第一个满足谓词条件元素的迭代器

16、bind1stbind2nd

两个函数都能把二元函数 转换一元函数。元指的是参数的数量。

转换的原理就是bind(绑定),他们能固定二元函数其中一个参数具体的值是多少,从而把他变成了一元函数。

而,bind1stbind2nd的区别就是固定的是哪个参数。前者固定第一个参数,后者固定第二个参数。
下面举个例子:

vector<int< vec = { 1, 2 ,2, 3, 4, 5 };

auto iter1 = find_if( vec.begin(), vec.end(), bdin1st(less<int>(), 2) );
cout << *iter1 << endl;

auto iter2 = find_if( vec.begin(), vec.end9), bind2nd(less<int>(), 2) );
cout << *iter2 << endl;

输出为:

3
1

上面对find_if的调用除了第三个参数bind1stbind2nd不一样以外,其他完全一致。但输出的结果不同。

  • bind1st固定less个参数为2,所以找的是第一个满足 2 < value 的数。
  • bind2nd固定less个参数为2,所以找的是第一个满足 value < 2 的数。

bind1stbind2nd的意义

bind1stbind2nd为那些只涉及一个容器泛型算法提供一个机会,让他们也能使用二元函数
啥???没有他们就不能用二元函数了吗?
没错,确实不能。你想啊,二元函数须要传进去两个参数,那你只涉及一个容器怎么传两个参数进去?
所以只能由bind1stbind2nd帮助他们固定好一个参数,把二元函数转化成一元的,这下就能用了。



17、综合标准库函数对象,泛型算法,bind1st bind2nd可以用简洁的语句做到一些事情

17.1 统计大于1024的值有多少个
count_if( vec.begin(), vec.end(), bind2st(greater<int>(), 1024) );
17.2 找到第一个不等于"pooh"的字符串
find_if( vec.begin(), vec.end(), bind2nd(not_equal_to<string>(), "pooh") );
17.3 把所有值乘2
transform( vec.begin(), vec.end(), bind2nd(multiplies<int>(), 2) );

18、for_eachtransform的区别

先明确这俩是干啥的。

  • for_each:对前两个参数指定范围内的元素,进行第三个参数表示的操作。
  • transform:对前两个参数指定范围内的元素,进行第四个参数表示的操作,结果存储在第三个参数表示的容器里,不改变原容器的元素。

已经可以看出区别了,for_each只涉及到一个容器,而且会修改原容器;transform涉及多个容器,只是访问原容器,结果存放在其他容器。

好,接下来详细分析一下transform的用法。

transform

第一个重载版本:
template <class InputIterator, class OutputIterator, class UnaryOperation>
  OutputIterator transform(InputIterator first1, InputIterator last1,
                            					OutputIterator result, UnaryOperation op);

图示:
在这里插入图片描述

  • 前两个参数依旧指定范围
  • 第四个参数指定要执行的操作
  • 第三个参数指定存放结果迭代器的起始位置
第二个重载版本:
template <class InputIterator1, class InputIterator2,
          class OutputIterator, class BinaryOperation>;
OutputIterator transform(InputIterator1 first1, InputIterator1 last1,
                           	 		InputIterator2 first2, OutputIterator result,
                            									BinaryOperation binary_op);

图示:
在这里插入图片描述

  • 第五个参数是一个二元算子(函数 或 谓词?总之是二元的)
  • 前两个参数依旧指定范围,其中元素做二元算子的个参数
  • 第三个参数是指向另一个容器的迭代器,其中元素做二元算子的个参数
  • 第四个容器指定存放结果迭代器的起始位置

下面举例同时说明上述两个重载版本:

#include <iostream>     // std::cout
#include <algorithm>    // std::transform
#include <vector>       // std::vector
#include <functional>   // std::plus

using namespace std;

int op_increase (int i) { return ++i; }

int main ()
{
	vector<int> foo = { 10, 20, 30, 40, 50 };
	vector<int> bar;

	bar.resize(foo.size());                          分配空间

	transform(foo.begin(), foo.end(), bar.begin(), op_increase);
													bar: 11 21 31 41 51

	// std::plus adds together its two arguments:
	transform( foo.begin(), foo.end(), bar.begin(), foo.begin(), std::plus<int>() );
													foo: 21 41 61 81 101

	cout << "foo contains:";
	for (auto it=foo.begin(); it!=foo.end(); ++it)
		cout << ' ' << *it;

	cout << '\n';
	return 0;
}

输出:

foo contains: 21 41 61 81 101

19、return语句中的string加法表达式的求值顺序

string rep(const Query& lhs, const Query& rhs)
{
	return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")";
}

上面的return中,有多个string相加,其求值是 从右往左执行 的,也就是rhs.rep()先于lhs.rep()被调用。

20、set_intersection标准库算法

默认版本:

template <class InputIterator1, class InputIterator2, class OutputIterator>
  OutputIterator set_intersection (InputIterator1 first1, InputIterator1 last1,
                                   InputIterator2 first2, InputIterator2 last2,
                                   OutputIterator result);

他接受5个参数,前四个迭代器表示两个输入序列,最后一个参数表示目的位置
该算法把前两个输入序列中共同出现的元素写入到目的位置中。

使用示例:

set_intersection( left.begin(), left.end(), 
				  right.begin(), right.end(), 
				  inserter(*ret_set, ret_set->begin() );

上述调用中,传入的最后一个参数是一个插入迭代器,当set_insertsection向该迭代器写入内容时,实际上是向ret_set插入一个新元素

扩展版本:

template <class InputIterator1, class InputIterator2,
          class OutputIterator, class Compare>
  OutputIterator set_intersection (InputIterator1 first1, InputIterator1 last1,
                                   InputIterator2 first2, InputIterator2 last2,
                                   OutputIterator result, Compare comp);

相比默认版本多了一个参数,这最后一个参数是个函数指针或任何可调用对象,接受两个参数,返回bool值,用于自定义比较前两个序列中的元素。

21、智能指针的 删除器

21.1 shared_ptr的删除器在运行时绑定

在一个shared_ptr的生存期中,可以随时改变其删除器的类型,因此他的删除器是运行时绑定的。
正因此,shared_ptr的删除器不属于shared_ptr的成员,因为成员的类型在运行时是不可变的,所以不能直接保存删除器作为成员。
实际上,在调用shared_ptr的删除器时,是跳转到某个成员保存的地址,并执行对应代码,完成删除操作。

运行时绑定,shared_ptr使用户重载删除器更方便

21.2 unique_ptr的删除器在编译时绑定

unique_ptr的删除器类型在编译时绑定,后面再也无法改变。
unique_ptr有个构造函数专门用来绑定我们自定义的删除器,因此,unique_ptr的删除器unique_ptr的一个成员

编译时绑定,避免了间接调用删除器的运行时开销

例题:
DebugDelete是我们自定义的一个删除器。
在这里插入图片描述

22、打开命名空间

我们使用如下方法打开某个命名空间,并向其添加我们自己定义的内容:

namespace 命名空间的名字
{
	// ..........
}

花括号之间的任何定义都将成为命名空间std的一部分。
举个例子,当我们想为标准库hash模板定义一个特例化版本时:

打开 std 命名空间,以便特例化 std::hash
namespace std
{
	// ...........
}

23、

标准库算法都是函数模板

标准库容器都是类模板

24、类的static成员

static成员属于类,不是与对象,被类的所有对象共享,现有static成员后有对象,所以static成员要在类外初始化。

由于static成员属于类,不属于对象,所以static成员函数没有this指针。this指针是指向本对象的指针,正因为没有this指针,所以static成员函数不能访问非static的成员。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值