1.指针的引用
他也是引用,引用是特定内存块的别名
2.变量定义
更准确的说是内存使用约定,并为该约定命名
命名3.指向常变量的指针和常指针
有点拗口,都是指针,但对于所在内存块的使用约定不同。常变量指针只能用于指向常变量,和普通指针不同。常指针所在内存块有固定的数据不可更改。
4.引用类型做参数时,函数不会操作对应内存块的拷贝,而是直接去操作那块内存。
根据引用这个概念的解释,它是对内存块已有约定进行补充,从而形成了一个新版约定,因此在定义新版约定是必须有个老版约定做基础。引用并没有为问题的解决提供新的路径,只是一项新的便于操作数据和理解的特性。
5.extern "C":表示这一部分代码按照C语言的方式处理,比如c++中的mangling mechanism在c中就没有了,c++中的自动清栈式调用在c中也变成了手动清栈
static void fun(){}:static表示这个函数仅在本文件内有效
static int i;:表示该全局变量也是仅在本文件内有效
如果一个源文件想要引用其他源文件中的函数或者变量,需要在前面加修饰符 extern:
extern int i;extern int fun()
6.另外,c语言不支持函数重载,c++支持
#define PTM ;int b;
#define ASD int a PTM int c;
8.__cdecl是被调用者自行清栈,用汇编来看待这过程,类似于
push ebp
mov ebp,esp
sub esp,0C0h ;存放函数参数
push ebx
push esi
push edi
......
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
__stdcall 是调用者来负责清栈,它是在一堆pop之前就ret了
9.参数化宏#define macro(parameter1,parameter2) xxxxxxx 三个比较有用的符号:“"表示换行 “#x”将参数x转成字符串 "xx##yy"将两个参数xx 和yy连接成一个字符串
10.位运算符的记忆
&: 是按位乘
|: 是叠加后不透明
^: 叠加后透明,或者是按位加,不记录进位
19. 用system执行多条命令行语句system("cmd.exe /c echo test & echo hello world & @echo off"); &符号连接两条语句,echo off关闭命令回显, 前面加@表示 echo off这条命令自身也不显示。
20.%m.nf 格式化输出,m表示位宽,n表示小数点后显示几位,没有m则有几位就显示几位。
21.c++输入时设置分隔符的办法cin.ignore(1,',')1表示读入的数量,逗号表示终止字符,或者用scanf("%d,%d,%d",a,b,c);
22.c++的valarray数组类的用法
valarray<int> va(14)//定义一个含14个元素的数组
valarryay<int> va2=va[slice(start,size,stride)]//对数组进行分隔裁剪
va.apply(fun)//对数组元素应用函数,类似于foreach()函数
va.size(size,val)改变数组的大小,并将各个元素值设置为val
std::size_t lengths[]= {2,3};
std::size_t strides[]= {7,2};
std::gslice mygslice (start,
std::valarray<std::size_t>(lengths,2),
std::valarray<size_t>(strides,2));
如上可以对数组进行多维度的划分。
23.(c++11新特性)
可变参数数量(函数,宏,类模板,异常捕获):int printf (const char* format, ... );
#define VARIADIC_MACRO(...)
try{
// Try block.
}
catch(...){
// Catch block.
}
template<typename... Arguments>
void SampleFunction(Arguments... parameters);
typename... Arguments这一部分代表一个参数包,函数调用的时候参数包会被解包,e.g.SampleFunction<int, int>(16, 24)
我们可以用sizeof...运算符来计算代入的参数个数,e.g. int count=sizeof...(Arguments);
模板参数个数可变,和函数参数可变相结合时,可能出现两个省略号连在一起,加不加逗号均可
template<typename... Arguments>
void SampleFunction(Arguments..., ...){
}
具体应用:
template<typename... BaseClasses>
class VariadicTemplate : public BaseClasses...
template<typename... BaseClasses> class VariadicTemplate : public BaseClasses...{ public: VariadicTemplate(BaseClasses&&... base_classes) : BaseClasses(base_classes)...{ } };
变参数模板也支持偏特化
template<typename... Arguments>
class VariadicTemplate{
public:
void SampleFunction(Arguments... params){
}
};
template<>
class VariadicTemplate<double, int, long>{
public:
void SampleFunction(double param1, int param2, long param3){
}
};
几个相关的操作符sizeof... decltype()
int foo(int i) {
return i;
}
double foo(double d) {
return d;
}
template<typename T>
auto getNum(T t)->decltype(foo(t)) {
return foo(t);
}
上面是一个比较有意思的用法,auto配合decltype来产生可变返回值类型,这些机制都是编译时刻就能决定的,或者说这些所谓的新特性就是编译器这一部分人工推导完成的工作给实现,没错,这一些特性的实现都可以人工来根据当前编码环境所提供的信息来完成,比如decltype获取类型,我们自己来根据编码context一样能完成类型推导,所以这些特性都是编译时期的工作。
具体使用时要配合递归调用自身
24.右値引用
首先是何为右值,我的理解是那些没有名称的临时变量,比如,cout<<16<<"abcd";这句中16和"abcd"都是临时变量,右值引用的出现还是为了减少内在开销。
25.pair只是把不相关的元素组合起来,map相当于一个pair的集合。
map的emplace方法和insert方法作用类似 只不过insert功能更高级一些 e.g. mymap.emplace('z',100)
insert 可以单个插入也可以,一次插入多个.
single element (1) |
pair<iterator,bool> insert (const value_type& val); |
---|---|
with hint (2) |
iterator insert (iterator position, const value_type& val); |
range (3) |
template <class InputIterator> void insert (InputIterator first, InputIterator last); |
26.c++11也支持自动元素遍历的用法了,类似于for(auto& x:container)//x代表元素,container代表容器
27.
LPCSTR的中文解释
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )
#else
#define offsetof(s,m) (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
#endif
ptrdiff_t是一个机器相关的类型,通常用来保存两个指针相减的结果,通常这个类型为long int
volatile是一个变量修饰符,表示该变量的使用不可以被优化,因为涉及到多线程同步的问题
reinterpret强制类型转换,并不改变变量的二进制结构
在这个宏中将0强制转化为S类型的指针,意即该S型变量的起始地址为0,其后的成员地址的获取都是根据这个起始地址来获取,再对某个成员取址就是相对于地址0的偏移量,也就表示了该成员在整个类型中的相对偏移量,这是非常巧妙的地方。
28.个人认为的接口和类继承的区别,其实二者都可以实现相同的功用,那就是代码的复用和扩展,但是我们从字面意思上来理解继承,就是说继承并不是功能的简单堆砌,而是有着本质上相同的东西,比如我们对于继承惯常的描述:以汽车为基类,可以派生出,轿车,卡车,客车等等,再往下派生比如,轿车又可以分为A级,B级,C级等等,我们总可以从一个角度来对事物进行划分,这种的划分没有哪种是最正确的,甚至没有哪种划分是最合理的,但是划分的时候我们总是尽可能的选择一种相对而言贴近业务需要的划分方式,接着说,这种惯常的描述,这种的派生之所以有其合理性,是因为在本质上这些类的对象都是相同的,有着一脉相承的东西,他们都是汽车,但是接口就不同了,我们总是说一个类实现了哪些接口,而不是继承了某些接口,这样的一种叙述方式也表明了,我们用实现接口这样的形式来进行功能的拼接并没有继承的意味在里面,接口和实现接口的类并没有一个共有的本质。C++里面没有接口的概念,但这其实不影响我们用接口的方式来进行功能的拼接以及用接口的方式来看待C++中的一些继承,比方说,我们有一个虚基类,实际上只要是包含了纯虚函数的类,他们都是抽象基类,并且不能被实例化,为了实现类似于接口的方式来构造我们的类,我们可以定义一些抽象基类,然后再在其他的类中继承和实现抽象基类中的纯虚函数,那么这里的继承其实就没有之前的一脉相承的意味了,而是更加类似于 其他一些语言中的implement。我想以上就可以说明了传统意义上的继承和接口的关系。还有需要说明的是,接口实际上可以看作是一种规范,它使我们的代码在保持了封装独立的前提下,还可以对外提供友好的接口,如果是一个完全封闭的类,那就失去了类的意义了。
29.c++使用参数化列表的时候,classA(int a):m_a(a),base(a){cout<<this->a;}这个构造函数里面的this->a是正常使用的,如果在基类的构造函数中是使用this->a是不能正常使用的,因为基类的构造函数执行在先,而且如果基类中没有成员变量a,却在构造函数中使用了this->a这样是不合乎语法的,即使你已经知道派生类中会有这个成员变量存在.
30.关于define中的#和##
首先c语句中的""和宏中的""都会被当成是字符串,这会让#和##失效,#是让变量字符串化,而##是连结变量和宏展开中的字符
31.for(auto it:arr){}这是c++11中新增的数组集合遍历方式,for_each这是stl提供的方法
32.unique_ptr<int> pt(new int[100]);这是c++中提供的具有内存自动回收机制的指针
33.strtok可以相当于js中的split 用法比较特别
str="1,2,3,5,6,9,2";
pch=strtok(str,", ");
cout<<pch;
white(pch!=null)
{
cout<<pch;
pch=strtok(NULL,", ");
}
34.关于函数指针作为返回值的函数的定义
void (*func())(){}
紧跟func的一对括号中间是设置函数参数列表的位置,最后一对小括号是属于返回函数的参数列表,这个形式可以拆开来看1.先定义返回値类型和函数名 void (*func)()1.给函数加上参数列表 这里和惯常的参数列表的位置不同,一般我们认为参数列表是放在最后的或者说是花括号之前的,其实他的规则不是这样,而是参数列表紧跟函数名,因此这里需要void(*func())()这样的形式来添加参数列表,没有参数也要保留一对小括号
同样的还有返回数组的引用的函数
int (&foo()) [10]{}, 也体现了参数列表紧跟函数名称的特点。
35.const_cast<char*>可以将非常量値变成常量值。
36.一直不明白strchr是何缩写,现在想来应该是string charater,即在string查找一个character,返回值和即character在string中的位置
37.同样的道理strstr就是在目标字符串中查找子字符串而不是一个字符,返回値同样是一个位置
38.fseek更改文件指针的位置 ftell得到文件指针相对于初始位置的偏移量,这两个配合来得到文件的大小
39.c++可变参数
#include <stdio.h> #include <stdarg.h> double average(int num,...) { va_list valist; double sum = 0.0; int i; /* initialize valist for num number of arguments */ va_start(valist, num); /* access all the arguments assigned to valist */ for (i = 0; i < num; i++) { sum += va_arg(valist, int); } /* clean memory reserved for valist */ va_end(valist); return sum/num; } int main() { printf("Average of 2, 3, 4, 5 = %f ", average(4, 2,3,4,5)); printf("Average of 5, 10, 15 = %f ", average(3, 5,10,15)); }
va_start 初始化可变参数列表
va_list 表示参数列表
va_arg 从va_list中取一个参数
va_end 清理参数列表的内存
40. __cdecl, __stdcall, __fastcall是三种不同的调用约定, __stdcall是由被调用者清楚参数栈,函数最后一句执行retn X用X指示清除的pop的字节数,
__cdecl,这指示由调用者清理参数栈,这是为什么在函数调用结束后,会有 add esp,4 add esp, 8这样一类的语句, __cdecl是默认的函数调用约定
__fastcall,指示参数是由寄存器传递的,也不存在谁清理栈的问题了
41.c++14也是支持闭包的,如下
auto a = [](auto n){ return [&](auto i){ return n += i; }; }(1);
cout << a(2) << endl;
cout << a(3) << endl;
42.一直搞不清move semantics, 原来是在翻译上有一些偏差, 在这里move不是一个形容词,而是一个动词,动作的对象是semantics, 也就是说将语义移动了, 改变了, 那么语义还能移动吗
右值引用的引入只是为了将rvalue区分出来,这么一区分就是为了配合move constructor, 以前不做区分的,现在做了区分就是要通过move constructor减少内存copy提高性能
43. C++往往有一些琐碎的限制规则,比如常对象,你不能调用const object的非const成员函数,而必须使用const 成员函数, 这大概是一种保护机制
44. 解决了一些理解上的偏差, rvalue reference 只是将可能被销毁的temporary object利用起来,减少了内存的copy和alloc 这里注意 "asdfasdfa" 对于const char *类型的temporary variable仍然要使用const char*&&的方式来使用,也就是说rvalue reference 并不能超越const的限制
45. 一个关于是左值还是右值的疑惑
void printRef(const char*&& str) {}
printRef("asdfasdfad");
参数"asdfasdfad"到底是右值还是左值,按照之前的理解, 这个字符串是一个临时变量应该是右值,其实不然,这里的参数实际上是一个const char *类型,也就是说,这个并不是一个临时的变量,只是他的值并不允许被改变
右值可以认为是expression所产生的一个临时的值