c++学习笔记(三)—— 语法/关键字
语法/关键字
auto
通过变量的初始值来推断变量的类型
逗号运算符
逗号运算符的结果是右侧表达式的结果
显式转换
语法:cast-name
cast-name:
- static_cast: 任何具有明确定义的类型转换,只要不包含底层const,都可以用。可以通过void*实现类型拆箱封箱操作
- dynamic_cast: 将基类的指针或引用安全地转换成派生类的指针或引用
- const_cast: 只能改变运算对象的底层const。比如const char*转string会报错
- reinterpret_cast: 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作
decltype
获取指定变量类型
int a = 0;
delctype(a) b = 0; // 等于int b = 0
bind
#include<functional>
using std::placeholders::_1;
bool Check(string &s, size_type sz);
auto check = bind(Check, _1, 6);
check(s); // = Check(s, 6)
ps: 拷贝引用参数的时候,需要用ref
ostream& print(ostream& os, char c)
{
return os << c;
}
bind(print, ref(os), _1, ' ');
mem_fn
在functional
库里
Math obj(1, 2);
auto f = mem_fn(&Math::GetY);
cout << f(obj) << endl;
引用界定符
如果对临时变量调用方法时,会导致语句的失效,比如下面情况:
Tracer t1("Hello");
Tracer t2("cpp");
printf("-----------------\n");
(t1 + t2).setTest("How about setText");
printf("-----------------\n");
(t1 + t2)返回的是一个临时的右值对象,运行完毕后销毁对象,然后这个方法就没啥用了。
引用界定符就是对非static函数加一个限制,使得右值对象不能调用setText方法。手段也很简答,只要在方法签名后面加一个&,就可以通知编译器,这个函数只对左值(引用)有效:
//这里的&表示:只有可修改的左值能调用此函数
void setTest(const char *text) & {
printf("%d => %s\n", m_text.c_str(), text);
m_text = text;
}
注意点1:这个符号和const可以同时用,但是const必须在&前
注意点2:只能用于非static的成员函数
花括号赋值
StrVec &StrVec::operator=(initializer_list<string> li)
{
// 分配内存空间并从给定范围内拷贝元素
auto data = alloc_n_copy(li.begin(), li.end());
free();
elements = data.first(); //更新数据成员使其指向新空间
first_free = cap = data.second;
return *this;
}
重载后置递增运算符
后置和前置重载的都是同一个符号,但是参数上有点差别:编辑器会提供一个0的形参
StrBolbPtr operator++(int); //后置运算符
StrBolbPtr& operator++(); //前置运算符
类型转换运算符
可以让类类型隐式转换成自定义的类型
class SmartInt
{
int val = 0;
public:
operator int() const {return val;}
};
隐式转换在某些情况下可能会出现莫名其妙的bug,所以可以改成显示转换
class SmartInt
{
int val = 0;
public:
explicit operator int() const {return val;}
};
SmartInt si = 3;
static_cast<int>(si) + 3;
allocator
对new和delete使用上的扩展:在内存分配时,和对象构造分开
使用:
- allocator
a - a.allocate(n)
- a.deallocate(p, n)
- a.construct(p, args)
- a.destroy(p)
- uninitialized_copy(b, e, b2) - 迭代器拷贝
- uninitialized_copy(b, n, b2) - 批量拷贝
- uninitialized_fill(b, e, t)
- uninitialized_fill(b, n, t)
NODEBUG 预处理变量
如果定义了NODEBUG,assert将什么也不做
- __func__: 当前函数名
- __FILE__: 存放文件名
- __LINE__: 当前行号
- __TIME__: 存放文件编译时间
- __DATE__: 存放文件编译日期
constexpr
用来表达在编译时就能算出来的值
控制浮点数打印精度
cout.precision(3);
cout << cout.precision() << " " << sqrt(2.0);
// 输出; 12 1.41
noexcept
c++11关键字,说明修饰的函数不会抛出异常
void recoup(int) noexcept;
void recoup(int) noexcept(true);
void recoup(int) throw(); //c++早期写法,和上面等价
- noexcept(e):如果e调用的所有函数都做了不抛出说明,且e本身不含throw语句时,该表达式为true
noexcept有两层含义:当跟在函数参数列表后面时,他是异常说明符;而当作为noexcept异常说明的bool实参出现时,它是一个运算符
void recoup(int) noexcept(noexcept(recoup(3.14)));