虽然整理成博客,很浪费时间,不过,每次忘记后,再捡起来,还要好久,还是继续把笔记整理成文章
该文不完全,遇到其他的语法再添进来
参考
C++中文版【王刚 杨】
注意
- 分号;表示语句结束,必不可少
- 注释
    - 单行注释 //开头 - 继承自C语言/*和*/之间,单行或多行,不能嵌套
- 缩进自由,{可以放在函数/while/…等的同行也可以下一行
- 大小写敏感
- 变量名一般用小写字母, 用户自定义的类一般大写字母开头, 多个单词组成的,单词首字母大写或下划线隔开
- 变量在用之前定义/声明就可以,不用像Fortran一样在开头定义
- 使用标准库中的命令空间中的对象,使用域操作符::,如std::cin
 可以使用using std::cin,之后就可以直接使用cin替换std::cin
- 引入了#include<iostream>后, 这两个标准库#include<string>#include<cctype>没引入也可以使用相应std中的变量
- 语句以;结束,可以用空语句. 多加;的问题:while( condition) ;表示循环体是空的
- 语句块用{}括起来,{}外面不加;,空块{}也表示空语句
- 在if的{}定义的变量、引用等操作,在{}外无效
头文件
头文件通常包含哪些只能被定义一次的实体,如类、const、constexpr变量等.
头文件一旦改变,相关源文件必须重新编译以获得更新过的说明
- 头文件不建议包含using声明,可能会和代码中的变量冲突
- c++新风格不包含拓展名.h/.hpp/hxx就是#include<iostream>
- C++标准库也兼容来C语言的辨准库,C语言的头文件如name.h,C++将这些文件命名为cname,即去.h加c,如C语言中的ctype.h在C++中就是cctype
 在cname中定义的类总能在命名空间std中找到. 而如果使用name.h文件,则需要额外记哪些是从C语言继承的,哪些是C++特有的,一般使用cname的形式
预处理器preprocessor
在编译前预处理源码,如#include就会用头文件的内容代替#include
可以使用预处理的功能确保头文件只被引入一次,即头文件保护符功能
整个程序每个头文件的保护符必须唯一,通常以头文件中类的名字来构建保护符的名字,以确保其唯一性,为了避免和程序的其他实体发生名字冲突,一般把预处理变量的名字全部大写.
//没有定义SALES_DATA_H 则包含下面内容; 第二次include时已经定义了就不包含了
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data{
      std::string bookNo;
};
#endif
其他
C++对C的拓展
命名空间(见下)
结构体struct可以定义函数体
C++比C有更严格的类型转换
在C++,不同类型的变量一般是不能直接赋值的,需要相应的强转
typedef enum COLOR{ GREEN, RED, YELLOW } color;
int testN(){
	color mycolor = GREEN;
	mycolor = 10;
	printf("mycolor:%d\n", mycolor);
	char* p = malloc(10);
	return 0;
}
c语言可以编译上面代码,但是c++报错
(base) cndaqiang@macmini day1$ g++ main.cpp
main.cpp:36:12: error: assigning to 'color' (aka 'COLOR') from incompatible type 'int'
        mycolor = 10;
                  ^~
main.cpp:38:8: error: cannot initialize a variable of type 'char *' with an rvalue of type 'void *'
        char* p = malloc(10);
              ^   ~~~~~~~~~~
2 errors generated.
需要我们严格写成
	mycolor = (color) 10;
	char* p = (char*) malloc(10);
三目运算符
- c语言的三木运算符返回值为数据值
- c++返回的是变量本身(引用),是左值,可以被赋值(a>b?a:b)=100;
const
c++不必const创建内存区域,(在编译阶段,编辑器悄悄优化了代码,没有创建内存,实现了宏定义的效果,称为常量折叠). c中一定要创建空间.
c++只有在声明const变量为extern或者取地址操作是,编辑器才会分配内存
推荐在c++中使用const代替宏定义(#define):const有作用域(如在特定函数内部),const有类型,可以进行编译器安全检查
- c中const全局变量默认为外部链接,不同文件不能重名
- 
    c++中const全局变量默认为内部链接,不同文件可以重名 
 也就是说,在A文件中const int a;,在B文件中extern const int a;会报错,B无法使用a
 如果想把a变为外部属性,在定义和声明处都写extern,定义处要给具体的值extern const int a=10;
- 
    const修饰全局变量时c和c++都不可以修改 
 c将全局const变量存在只读数据段.
 c++只有在声明const变量为extern或者取地址操作是,编辑器才会分配内存
- const修饰局域变量时
 c存储const局域变量在栈区,可以通过指针间接修改
 c++用指针p指向const局域变量a后,*p能被改变,而输出的a和*(&a)不一定相同(见下),这是编译器的优化策略,使用volatile修饰a可以关闭优化,a==*(&a)
    //编译器优化(常量折叠)情况
    const int a=10;
    int *p = (int *) &a;
    *p=100; // 通过指针指向const局域变量,局域变量本身引用不会改变,使用地址引用发现变了
    cout << "a=" << a << ",*(&a)=" << *(&a) <<endl; //(这是编译器优化的结果(常量折叠),直接讲a替换成了10,而栈区地址的值*(&a)其实已经被改变了)
    //
    const int b=a; // 使用const常量初始化,输出b量不会被改变(常量折叠)
    p=(int *) &b;
    *p=100;
    cout << "b=" << b << ",*(&b)=" << *(&b) <<endl; 
    //----------------------------------------------------
    //编译器不优化情况
    //使用基础数据类型(int)和自定义类型(struct)初始化const局域变量, 编译器不会优化
    int c=10;
    const int d=c; //
    p=(int *) &d;  //常量会被指针改变
    *p=100;
    cout << "d=" << d << ",*(&d)=" << *(&d) <<endl;
    //
    //使用`volatile`修饰可以关闭优化
    volatile const int e=10;
    p = (int *) &e;
    *p=100;
    cout << "e=" << e << ",*(&d)=" << *(&e) <<endl;
引用/别名
- 变量名是一段内存空间的名字,引用是给空间取别名,两个名字都指向同一片内存区域
- 
    引用是c++对c的重要扩充,除了指针,引用是另一种传递地址的途径,(语法比指针更简单)也存在于其他语言中 
 可以说c++没有指针,虽然可以用指针(兼容c),c++用引用
 就是用于函数传递参数的,形参会创建空间(指针也有4/8字节),而传递引用类型就不用创建空间了
- 用法type & a = b,此处的&近作标记,不是取地址
- 引用创建时必须初始化,一旦初始化不能改变指向,常量创建时也必须初始化,
 其实本质上引用就是常指针,
 c++编译器会把type & ref = val;转为type * const ref = &val;
 会把ref=val2;转为*ref=val2;
- 引用必须引用合法的内存空间
int i;
int &i2 = i;      // 右边必须是一个对象, 左边的类型要与右边的对象类型一致, 可以理解为新建立个变量叫i2,不分配新内存而是将其内存地址 &i2 设置和i一样
i=10;
std::cout << i2;  //输出10
i2=20; //这是赋值操作,无法修改i2的指向. 另外,指针的赋值*p=20;,引用直接i2=20
引用传递参数和指针的对比:语法更简单
#include<iostream>
using namespace std; // using后,就可以直接用std中的变量endl了,不用std::endl
void fun_ref(int &a)
{
    a=200; //引用的方式,可以直接使用变量名
}
void fun_poi(int *a)
{
    *a=300; // 指针计算还要在用*
}
int main()
{
    int a=10;
    fun_ref(a);//引用的输入参数也不用变
    cout << "a'" << a << endl;
    fun_poi(&a);//指针要传递的是地址
    cout << "a'" << a << endl;
    return 0;
}
数组的引用
int arr[]={1,2,3,4,5};
#方法一,创建数组类型
typedef int(MY_ARR)[5];
MY_ARR &arrref=arr
#方法二,直接引用.常用
int(&arrref2)[5]=arr;
#方法三,创建引用数组类型
typedef int(&MY_ARR)[5];
MY_ARR arrref3=arr
指针的引用
char *p="123";
char* &p1 = p; //指针的引用
还是体现在函数穿参数的定义和使用更简单
#include<iostream>
using namespace std; // using后,就可以直接用std中的变量endl了,不用std::endl
void fun_ref(char* &tmp) // 这里加个&,就是说是char * 类型,也不用传递地址了
{
    char *p;
    p=(char *)malloc(64);
    memset(p,0,64);
    strcpy(p,"fun_poi");
    tmp=p; // 直接使用就好
}
void fun_poi(char* *tmp) 
{
    char *p;
    p=(char *)malloc(64);
    memset(p,0,64);
    strcpy(p,"fun_poi");
    *tmp=p;
}
int main()
{
    char *a=NULL;
    fun_ref(a);
    cout << "a'" << a << endl;
    fun_poi(&a);
    cout << "a'" << a << endl;
    //
    return 0;
}
数据类型
类型
算术类型
| 类型 | 尺寸 | 范围 | 
|---|---|---|
| char | 1 个字节(1 Byte = 8 bit) | -128 到 127 或者 0 到 255 | 
| unsigned char | 1 个字节 | 0 到 255 | 
| signed char | 1 个字节 | -128 到 127 | 
| wchar_t | 2 个字节 | 1 个宽字符 | 
| wchar16_t | 2个字节 | Unicode 宽字符 | 
| wchar32_t | 4 个字节 | Unicode 宽字符 | 
| int | 4 个字节 | -2147483648 到 2147483647 | 
| unsigned int | 4 个字节 | 0 到 4294967295 | 
| signed int | 4 个字节 | -2147483648 到 2147483647 | 
| short int | 2 个字节 | -32768 到 32767 | 
| unsigned short int | 2 个字节 | 0 到 65,535 | 
| signed short int | 2 个字节 | -32768 到 32767 | 
| long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | 
| signed long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | 
| unsigned long int | 8 个字节 | 0 到 18,446,744,073,709,551,615 | 
| float | 4 个字节 | 精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字) | 
| double | 8 个字节 | 双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字) | 
| long double | 16 个字节 | 长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。 | 
| bool | true/false | 
类型转换
向对象进行赋值
int i;
i=3.14; // i的值为3
double pi=i; //pi的值为3.0
自面值常量literal
常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。
常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。
常量就像是常规的变量,只不过常量的值在定义后不能进行修改。
整型
- 20十进制,- -42中的- -不在字面值内,代表对字面值- 42取负
 通过后缀指定类型 -- 20u,- 20Uunsigned无符号整数 -- 20l,- 20Llong长整数 -- 20ll,- 20LLlong long整数 -- 20ulunsigned long 无符号长整数
- 024八进制
- 0x16十六进制
 - std::cout << 20 <<" "<< 024 <<" " << 0x14 << std::endl;,输出- 20 20 20
浮点数
- 1.234
 后缀指定类型:- f或- Ffloat型,- l或- Llongdouble, 示例- 1.234f
字符和字符串
- 'a'字符字面值
 前缀指定类型:- u,- U,- L,- u8分别代表- char16_t,- char32_t,- wchar_t,- char
- "hello world"字符串字面值,编译器在字符串结尾添加空字符- '\0',因此实际长度比内容对1
 - 'A'和- "A"的区别是- 'A'是字符,- "A"是字符数组,包含- A和- \0
转义序列
| 转义序列 | 含义 | 
|---|---|
| \\ | \ 字符 | 
| \' | ’ 字符 | 
| \" | ” 字符 | 
| \? | ? 字符 | 
| \a | 警报铃声 | 
| \b | 退格键 | 
| \f | 换页符 | 
| \n | 换行符 | 
| \r | 回车 | 
| \t | 水平制表符 | 
| \v | 垂直制表符 | 
| \ooo | 一到三位的八进制数 | 
| \xhh . . . | 一个或多个数字的十六进制数 | 
bool型
      -     true
      -     false
类型别名
给类型起个别名, 可以用别名替代原类型名进行使用,但是不是简单换为之前的样子
typedef int in1, in2;   // in1,in2  都是int的别名
in1 ji;
in2 j2;
j2=10;
typedef int *p; // p是int*的同义词
c++11可以使用别名声明来定义类型的别名
using SI=Sales_item;
复合类型或常量的别名可能会产生意想不到的后果, 这里不能简单替换
typedef char *pstring; // pstring是指向char的指针
const pstring cstr=0;   //  const pstring是常数指针类型, 等价于const *chat cstr
// 上一条就不能简单替换为const char * 
const pstring *ps; // ps是是个指针,指向“常数指针”
const char * ps2; // 这里的ps2是指针,指向常变量 
auto类型
c++11标准,根据初值(表达式)内容分析所属类型,因此一定有初值
auto  a=i+j; //根据 i+j的类型推断出a的类型
auto会忽略掉顶层const,同时底层const则会保留下来
const int ci=10;  // ci顶层const,常变量
auto e=&ci; // e是底层const,是指向整型常量的指针
auto也可以和引用一起用
auto &g=ci; 
const auto &j=42; // 可以为常量, 指向表达式的常量引用
decltype
c++11标准,很具表达式类型判断类型,单不赋初值
decltype(f()) sum=x; //sum的类型为函数f的返回类型
如果是引用的类型,则必须在定义时给出初值
const int ci=0, &cj=ci;
decltype(ci) y=ci; //   必须给y赋初值
另外decltype((变量名))双层括号的结果永远是引用,而decltype(变量名)只有在变量是引用是才是引用类型
变量
定义
类型说明符 变量名1,变量名2,变量名3=value,...;
如果类型说明符是类,或者其他库中的命名空间也是如此,如
int a;
Sales_item item; //    类
std::string book("abcdefg") // 库类型std::string
初始化的方式
int i = 0;
int i = {0};
int i{0};   // 列表初始化, 需要C++11标准支持,即编译参数为 g++ -std=c++11
int i(0);
声明
c++将声明和定义区分开. 声明使程序了解变量的类型,定义则是创建变量.
变量只能被定义一次,可以多次声明.
声明示例
extern int i;
//如果用extern的语句又赋来初值,则视为定义,不再是声明
extern int i(3);
指针
指针是一种对象,值是指向不同的对象,可以随时更改其值即指向不同的对象,引用只是起一个名字,且只能指向一个对象.
指针的声明用*指针名,声明的类型必须同将来指向的对象的类型名一致
int i;
int *pi = &i;
std::cout << *pi;  //用*返回指针指向的对象的值
std::cout << pi;  //不加*返回指针的内容,即相应对象内存地址,等价于 std::cout << &i
std::cout << &i;  //返回i的地址, 即pi的内容
std::cout << π  //返回pi的地址
解引符号*作用到指针上就等价于指向的对象本身,可以进行赋值等操作, 
在声明时*用于定义变量是指针*,指向指针的指针**,指向(指向指针的指针)***
*是运算符, int *pi,int* pi,int * pi是等价的
cndaqiang: *代表该对象的内容, int *p,表明p的内容是个整型,即p是指针,后面调用这个指针的内容也要*p, 也可以理解为int*定义的是整型指针
&提取地址符号
int i;
int *pi = &i;
*pi=100;    // 可以这样修改原始变量空间
std::cout << i;
空指针,不能把整型等类型直接赋值给指针, 空指针if ( pi )条件返回false
pi=NULL
pi=0  //    可以赋值为0,等价于NULL,但不能赋值1,2...以及整型变量
i=0
pi=i // 这是错误的
pi=1 // 这是错误的
void类型的指针,可以指向任意类型的对象,但是他和普通指针不一样,只能用于指针比较、作为函数的输入或输出、或者赋予另一个void型指针. 不能操作void指向的对象, 也不能了解对象的类型
void *vp=&i;
std::cout << vp;
//std::cout << *vp; //错误用法
指向指针的指针,使用多个*进行定义/声明,使用多个*去取出存储的对象
int i;
int *pi = &i;
int **ppi = π
int ***pppi = &ppi;
//可以继续无限套娃
i=10;
std::cout << i << *pi << **ppi << ***pppi ;
对指针进行引用,不是创建新的指针,而是指向想应指针的引用,引用后的使用和被引用的指针相同,就是起个别名而已
int ***&rp=pppi;
std::cout << i << *pi << **ppi << ***pppi << ***rp;
常变量const
定义
const int i=20;
常量的引用类型必须其引用的对象相同.
第一种例外, 初始化常量引用时允许用任意表达式作为初值,只要该表达式的结果可以转换成引用的类型即可,编译器会把计算结果保存到一个临时量对象,然后引用指向此临时量.但是非常量的引用不可以引向表达式
const int i=11;
int i2=10;
const int &ri=i;
const int &ri2=i2; // 表达式可以转换也可以
const int &ri3=34; // 表达式可以转换也可以
int &r4=ri*2; //错误语法, 非const的引用是不可以的
默认的常量只能被本文件访问,在声明和定义时添加extern允许全局访问
extern const int i=256; //定义
extern const int i ; //头文件中进行声明
底层const:对象的内容可以被改变, 即指向常变量的指针也必须加const进行定义
const int *ip;    //底层const,可以改变ip的值, ip指向常变量
顶层const:对象的内容不可以被改变
- 常指针,指针指向固定的指针
- 常变量,不能改变值
    int *const pc=&i; // 顶层const, pc指向&i,不可以被改变 const int c=42; //顶层const, 常变量
引用常变量也要加上const
const int &ri = i;
常量表达式:在编译过程就能得到计算结果的表达式
const int max_kpoint=256;     //    常量表达式
const int limk=max_kpoint+1;  //常量表达式,编译时就可以确定的值是常量表达式
const int sz=get_size();      // 运行时才能得到该常量的具体数值
c++11新标准规定允许将变量声明为constexpr类型,以便有编译器验证变量的值是否是常量表达式.
constexpr支持的类型都是字面值类型,不能是用户自定义的类型.
constexpr声明中如果定义了指针,则仅对指针有效, 即顶层const
const int *ip;    //指向整型常量的指针
constexpr int *ip;    //指向整型的常量指针
数组
数组的大小固定,如果不清楚元素的确切个数,建议使用vector
定义数组时必须指明数组的类型,不允许使用auto关键词根据初始判断类型.
数组的元素应为对象,因此不存在引用的数组. ?应该是元素不可以是引用,但可以被引用
使用a[i]下标访问
int a[10]; //10个元素的整数数组
int *b[10]; //10个元素数组,数组每个元素的类型为整型的指针
定义和初始化
int a[3]={0,1,2};
int b[ ]={0,1,2};// b[3]={0,1,2}
int c[5]={0,1,2}; // 缺省有默认值, a[5]={0,1,2,0,0}
string d[3]={"hi"}; //string也可以
char a[]={'C','+','+'};
char a[]={'C','+','+','\0'}; //包含显示的空字符
char a[]="C++"; //字符数组特殊,可以这样初始化,包含显示的空字符`\0`,此时如果指定a的大小应至少为4
int *pi[10]; //指针数组
int &pr[10]; //错误,元素不能为引用
int a[2][3]={
      {1,2,3},
      {4,5,6}
};
int a[2][3]={1,2,3,4,5,6};
引用数组,指向数组的指针
int arr[10];
int (&arrRef)[10] = arr;
int (*Parray)[10]=&arr;
操作
数组不可以直接拷贝和赋值,数组名一般表示位置a+4则是第5个元素的位置
int a2[]=a; //错误
a2=a;//错误
指针和数组
在很多用到数组名字的地方,编译器会自动替换为指向数组首元素的指针
string nums[]={"one","two","three"};
string *p=&nums[0];
//等价于
string *p=nums;
[略]迭代器
运算
注
- i=f1()+f2(),- f1和- f2的执行顺序未知
- 算术运算符+-*/%加减乘除取余
- 逻辑<,<=,>,>=,==,!=,&&,||
- 允许赋值a=b=0,即b=0;a=b
- 前置计算++i返回加完的i,
 后置计算i++返回加之前的i(这种返回的是运算之前的内容需要额外的存储,除非必要时不建议)
- 后置递增运算符优先级高于解引用运算符*pbeg++等价于*(pbeg++),可以用于循环提取元素
- 条件运算符,条件?真返回的表达式:否返回的表达式cond?expr_true:expr_false;
- sizeof(type);sizeof expr不计算表达式具体的值,仅返回对象/表达式结果占用的空间
- 逗号运算符,,执行左边的计算,执行右边的计算,最后返回左边的值

位运算符
| 运算符 | 操作 | 示例 | 
|---|---|---|
| & | p&q,p和q全为1时返回1,否则返回0 | |
| | | p&q,p或q至少一个为1时才返回 1 | |
| ^ | 异或运算, p&q,p和q相同返回1,不同返回0 | |
| ~ | 取反 ~q | |
| << | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0) | |
| >> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃 | 
使用异或运算可以找到特殊数据,如
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素@LC
class Solution { public: int singleNumber(vector<int>& nums) { int len = nums.size(); if ( len == 1 ) return nums[0]; int result = nums[0]^nums[1]; for ( int i = 2 ; i < len ; i++ ) { result ^= nums[i]; } } };
命名空间namespace
避免重名问题,使用::使用同名变量,命名空间中的名称可以是符号常量、变量、函数、结构、枚举、类和对象等等
创建命名空间
- 命名空间只能全局范围内定义
- 命名空间可嵌套命名空间
- 命名空间是开放的,可以随时把新的成员加入已有的命名空间中.
 即命名空间可以拆成多块,声明和实现可以分离(可以在前面声明,又在后面实现)
- 无名命名空间,等价于c中的static修饰静态全局变量,属于内部链接属性,仅在当前文件可用
示例
#include<iostream>
using namespace std; // using后,就可以直接用std中的变量endl了,不用std::endl
namespace A{
    int a=10;
}
namespace B{
    namespace A{
        int a=110;
    } // 命名空间嵌套
    int b=120;
    void fun(int i); //这里仅进行定义
}
//随时补充命名空间
namespace A{ int b=20;}
namespace B{
    void fun(int i)
    {
        cout << "Input para is " << i <<endl;
    }
    void fun() //同名函数参数类型/参数顺序/返回值也可以,类似Fortran中的interface实现的同名函数重构,C++喜欢翻译为函数重载,都是Function Overloading
    {
        cout << "Fun w/o para" <<endl;
    }
}
int a=-10;
void test(){
    B::fun(B::A::a);
    B::fun(A::a);
    int a=1010;
    B::fun(a); // 直接用a按照局域>全局去寻找
    B::fun(::a); // ::并且不指出名字,直接从全局找
    using B::fun; // using后,就可以直接用名字了
    fun(10);
    fun();//支持函数重载
}
使用命名空间
- 命名空间重命名namespace newName=oldName;
- 使用命名空间的所有变量using A,则A中的所有变量都可以直接用变量名使用
- using A::a,可以直接用a替代- A::a.在相同作用域内,不能using不同命名空间中相同变量名的变量,也不能和现有声明的变量冲突
 using的函数支持函数重载,会参数类型/参数顺序决定具体调用形势
语句
while
while (condition)
      statement;
while (condition)
{
      statement1;
      ...;
      statement2;
}
读入不确定数量
      int i,sumi;
      sumi=0;
      while(std::cin >> i)
      {
          sumi=sumi+i;
      }
      std::cout << "Input sum is:" << sumi <<std::endl;
do while
do
      expr;
while(condition);
for
for ( int val=1; val <= 10; ++val)
{
      statement;
}
先判断条件,再执行第一次循环,即初始设置不符合条件也不会执行
int n=10;
//这里的 i < n 是每一次循环后根据新的n重新判断的
for ( int i = 1; i< n; ++i)
{
      statement;
      n=n-1
}
if
if (condition) {
      statement;
}
else {
}
switch
switch (var) {
      case value1:
            expr;
            expr;
            break;
      case value2:
            expr;
            expr;
            break;
      case ...
      default: /* 可省略,默认情况*/
            expr;
            break
}
如果不加break,则可以在随后一个条件后加break以及统计语句,实现多种情况的统计
跳转
- break终止循环- while,do while,for,switch
- continue,进入下一个循环,适用于跳到- while,do while, for的下一个循环
- goto跳转到同一函数内的另一条语句- goto label; label: expr;
- return可以直接跳出整个函数体的执行,即使套了再多层的循环也会终止
错误捕获
throw略
try略
函数
- 参数之间逗号隔开,就算类型相同也要隔开int fun(int a, int b)
- 函数最外层作用域中的局域变量不能使用与函数形参一样的名字?
- 函数内定义的对象尽在函数的作用域内可见
- 仅在第一次定义初始化,后续处理的值保留,就是Fortran的SAVE属性,用static定义
- 调用之前要声明,可以多次声明,可以写在头文件中,函数体和调用可以不在同一个文件,可以分开编译
参数
- 传递对象名时,是传值调用,形参和实惨相互独立
- 传递的内容是指针时,修改了指针指向的内存区域,可以实现更改函数外的对象
- 因为数组不能被拷贝,所以传递的通常是数组的地址(即以指针的方式进行传递)
- main的参数
    int main(int argc, char *argv[]) { ... }argv[0-...],
- 默认参数,可以调用时可以省略实参
    typedef string::size_type sz; string screen( sz ht=24, sz wid = 80, char backgrnd = ' ' )
函数重载
同一作用域内几个函数名字相同单参数不同时,称为函数重载, 就和Fortran的interface的同名函数重构功能
类
定义
注意结束定义处的分号
struct 类名
{
      类体;
      //c只能定义成员变量(函数指针变量可以),不能定义成员函数
      //c++可以定义成员函数
};    // 此处分号不可缺少
也可以通过class创建
class 类名
{
      类体
};    // 此处分号不可缺少
struc和class的区别在于:对于structure在定义似一个访问说明符之前的成员是public的,而class是private的. 如果希望成员都是public时使用structure,希望成员时private使用class
创建对象
类名 对象(变量)名;
//c中是struct 类名 对象(变量)名;
//c++中不用加struct
使用
对象.对象
标准库
iostream
c++为定义任何的输入输出(IO)语句,使用标准库来实现. 标准库的4个IO对象.
std::cin标准输入,istream类型的对象
std::cout标准输出,ostream类型的对象
std::cerr标准错误,
std::clog一般信息,
输入和输出以流的方式进行,比如std::cout << "Input num is:" << i <<std::endl;,就是把 "Input num is:",i,std::endl依次流向标准输出.
输出运算符<<左侧是ostream类型的对象(如std::cout),右侧是对象要打印的值,运算的返回值是左侧的对象,因此std::cout << A << B;等价于(std::cout << A) << B;,以及std::cout << A; std::cout << B;
输入运算符>>同理返回左侧的对象, std::cin >> A >> B;等价于(std::cin >> A) >> B;,以及std::cin >> A; std::cin >> B;
std::cin >> A 读入结束或者读入数据和A的类型冲突时,返回False,可以配合while,if等进行联合使用,读入不定数量的输入/对读入进行判断, Unix系统的文件结束输入为ctrl+d(输入完成,先回车在ctrl+d)
std::endl功能一是结束本行,可以用”\n”达到换行的功能, std:endl还有将内存缓冲区的内容刷到设备的作用. 类似于fortran中的flush(6), 便于找到bug的地方.
这些对象都来自std的命令空间namespace, 因此在使用的时候要用作用域算符::调用
#include<iostream>
int main()
{
      std::cout << "hello world" << std::endl;
      std::cout << "Input:" ;
      int i;
      std::cin >> i;
      std::cout << "Input num is:" << i <<std::endl;
      return 0;
}
string
因为某些历史原因,也为了与C兼容,所以C++语言中的字符串字面值char类型并不是标准库类型string的对象.切记, 字符串字面值与string是不同的类型
#include <string>
using std::string;
main()
{
      return 0;
}
定义和初始化
string是一个类,初始化这个类的对象, 通过()进行(操作计算出来的)直接初始化,使用=进行拷贝初始化
string s1; //默认初始化,s1是一个空串
string s2(s1); // s2是s1的副本,
string s3("hello"); //s3是hello的副本
string s2=s1; //  同上
string s3="hello"; //同上
string s4(n,'c'); //s4由n个'c'组成的串
操作
| 操作 | 含有 | 
|---|---|
| os<<s | 将s写到输出流os当中,返回os, 即 std::cout << si << sj; | 
| is>>s | 从输入流is中读取字符串赋给s,字符串以空白分隔,返回is, 即 std::cin >> si >> sj; | 
| getline(is,s) | getline就是从输入流is中读取一行(到换行符位置)赋给s(不包括换行符) 读到末尾时会返回False,其他情况返回true 和 >>是等价的,也可以和while连用 | 
| s.empty() | s为空返回true,否则返回 false | 
| s.size() | 返回s中字符的个数 | 
| s[n] | 返回s中第n个字符的引用,位置n从0计起,string和vector的下标只能用于访问,不能修改 | 
| s1+s2 | 返回s1和s2连接后的结果 | 
| s1=s2 | 用s2的副本代替s1中原来的字符 | 
| s1==s2s1!=s2 | 如果s1和s2中所含的字符完全一样,则它们相等; string对象的相等性判断对字母的大小写敏感 | 
| <,<=,>,>= | 利用字符在字典中的顺序进行比较,且对字母的大小写敏感 | 

for
C++11支持使用for处理string对象的每个字符,配合ispinct()等函数可以进行统计
      string s="hello123";
      for ( auto tmp : s)
            std::cout << tmp << "\n";
也可以用for改变
      string s="hello123";
      for ( auto &tmp : s) // 这里tmp是引用,指向了相应位置的地址
            tmp=toupper(tmp);
vector
- C++标准要求vectory能在运行时高效添加元素,因此和其他语言的定义时指明具体容量以加速计算相反,通常先建立空的vector,后期动态添加
    #include<vector> using std::vector;vector是类模版,(类、函数)模版不是类,而是为生成类或函数便携的一份声明,
 以vector为例,提供的信息供编译器创建类(实例化).这里其实是创建各种类型的组成个一个的类.vector<int> ivec; \\生成一个int元素组成的类,并创建相应的对象ivec vector<vector<string>> ivec; \\向量元素是vectory对象
初始化
 列表初始化是C++11的特性.
列表初始化是C++11的特性.
注意{}和()的区别
操作
添加元素
v2.push_back(i);   //加到末尾
//插入到第一个元素前面
v2.insert(v2.begin(),8);//在最前面插入新元素8。 
v2.insert(v2.begin(),3,8);//在最前面插入新元素{8,8,8}。 
//由于v.end() == v.begin()+v.size()
v2.insert(v2.end(),8);//在末尾插入新元素8。 

批量赋值
//赋值v[0-3](不包括v.begin()+4),到b[4-]
//复制到第k个(下标是k-1),v.begin()+k
copy(v.begin(),v.begin()+4,b.begin()+4);
v.begin()+v.size()等价于v.end(),完整复制即copy(v.begin(),v.end(),b.bengin())
排序
#include <algorithm>
std::sort (v.begin(), v.end());
删除元素
//删除第j个元素
auto iter = nums.erase(nums.begin()+j-1);
//删除num[j]
auto iter = nums.erase(nums.begin()+j);
函数传递
使用引用和传递地址的方式可以改变vectory的值, 传值调用无法改变原先的值
// 传值
int fun1( vector<int> a)
{
std::cout << "fun1" ;
 for ( int i=0; i<a.size(); i++)
{
        std::cout << a[i];
}
std::cout << std::endl;
return 0;
}
// 引用传入
int fun2( vector<int>& a)
{
std::cout << "fun1" ;
 for ( int i=0; i<a.size(); i++)
{
        std::cout << a[i];
        a[i]=199;
}
std::cout << std::endl;
return 0;
}
int main()
{
std::cout << "hi" <<std::endl;
vector<int> v={0,1,2,3,4,5,6,7,8,9,10};
int j;
j=fun1(v);
//引用传入才可以改变值
j=fun2(v);
std::cout << v[0] << std::endl;
map
C++map用法@W3Cschool
类似字典,使用关键词进行索引,关键词可以是多种类型
#include <map>
std::map<关键词类型,值类型> 变量名;
//如
std::map<int,int> mymap;
添加元素
std::map < int , std::string > mapPerson;
mapPerson.insert(pair < int,string > (1,"Jim"));
mapPerson.insert(std::map < int, std::string > ::value_type (2, "Tom"));
mapPerson[3] = "Jerry";
基本操作
- begin()返回指向 map 头部的迭代器
- clear()删除所有元素
- begin()返回指向 map 头部的迭代器
- clear()删除所有元素
- count()返回指定元素出现的次数,返回整数, 只能是0或1
- empty()如果 map 为空则返回 true
- end()返回指向 map 末尾的迭代器
- equal_range()返回特殊条目的迭代器对
- erase()删除一个元素
- find()查找一个元素
- get_allocator()返回map的配置器
- insert()插入元素
- key_comp()返回比较元素key的函数
- lower_bound()返回键值>=给定元素的第一个位置
- max_size()返回可以容纳的最大元素个数
- rbegin()返回一个指向map尾部的逆向迭代器
- rend()返回一个指向map头部的逆向迭代器
- size()返回map中元素的个数
- swap()交换两个map
- upper_bound()返回键值>给定元素的第一个位置
- value_comp()返回比较元素value的函数
[略]迭代器
其他
set
#include<set>
        int len = nums.size();
        if ( len == 1 ) return false;
//        set虽然可行但不快
        set<int> set_nums;
        for ( int i = 0; i < len  ; i++)
        {
          //插入新元素, 并判断是否存在
          pair<set<int>::iterator,bool>  pair1=set_nums.insert(nums[i]);
          if ( pair1.second == false ) return true;
        }     
本文首发于我的博客@cndaqiang.
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!