虽然整理成博客,很浪费时间,不过,每次忘记后,再捡起来,还要好久,还是继续把笔记整理成文章
该文不完全,遇到其他的语法再添进来
参考
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
,20U
unsigned无符号整数 -20l
,20L
long长整数 -20ll
,20LL
long long整数 -20ul
unsigned long 无符号长整数024
八进制0x16
十六进制
std::cout << 20 <<" "<< 024 <<" " << 0x14 << std::endl;
,输出20 20 20
浮点数
1.234
后缀指定类型:f
或F
float型,l
或L
longdouble, 示例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==s2 s1!=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的特性.
注意{}
和()
的区别
操作
添加元素
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或1empty()
如果 map 为空则返回 trueend()
返回指向 map 末尾的迭代器equal_range()
返回特殊条目的迭代器对erase()
删除一个元素find()
查找一个元素get_allocator()
返回map的配置器insert()
插入元素key_comp()
返回比较元素key的函数lower_bound()
返回键值>=给定元素的第一个位置max_size()
返回可以容纳的最大元素个数rbegin()
返回一个指向map尾部的逆向迭代器rend()
返回一个指向map头部的逆向迭代器size()
返回map中元素的个数swap()
交换两个mapupper_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 协议 ,转载请注明出处!