c++学习笔记(乱序版)
以下的文件名或者代码中的中文字符都是娱乐或者为了方便查找,大家写代码时推荐使用英文字符
变量的声明与定义
变量的声明和变量.cpp
#include<iostream>
using namespace std;
int i;//变量的定义
extern int j;//变量的声明,且声明的类型要与原定义的类型相同
extern double q=3.1475926;//赋值了就变成了定义而不是声明
int main(){
j++;
cout<<j<<endl;
system("pause");
return 0;
}
extern int x;变量的声明,且声明的类型要与原定义的类型相同
- 若给声明的变量赋值则变成了定义
two.cpp
#include<stdio.h>
int j=55;
void foo(){
printf("%d",j);
}
利用变量的声明可以使变量在不同的文件中传递数值!
const限定符
const限定符.cpp
#include<iostream>
#include<stdlib.h>
const int play=3;/*const会让play只在此项中可用,若想其他项可用,则加上extern
const的对象为项内的局部变量*/
extern const int play2=4;
//以下三个为原型
extern int bb;
extern float cc;
extern double dd;
using namespace std;
int main(){
system("pause");
return 0;
}
- const定义的常量为局部常量,其他的项无法使用,若是想让其他的项也可以调用,则需要加上extern。
- 类似于这样
int j;
的代码的原型是extern int j;
two.cpp
#include<iostream>
//extern const int play;这样会报错
extern const int play2;
using namespace std;
void foo(){
//不可用main
}
- 无法声明上一个项中的play变量,因为play是局部变量
- 其他项的函数名不要重复
引用
引用.cpp
#include<iostream>
using namespace std;
int main(){
const int aa=0;
const int &ddd=aa;//const引用必须引用const类型
//int &x=aa;非const引用不可以引用const类型
const int &y=42;//const引用可以引用常量
int 张飞=99;
const int &yy=张飞;//const可以是非const类型的别名
int &张翼德=张飞;//张翼德是张飞的别名
int c;
int &d=c;
int e;
//int &f;定义引用必须马山个初始化,否则报错
//int &f=10;别名必须是变量,因为是别名
张飞++;
cout<<张翼德<<endl;
system("pause");
return 0;
}
- 引用用的是取地址符&,但是不要跟指针搞混淆。
- 只能引用同类型的变量,const也是如此
- const引用可以引用常量
- 但是const可以是非const类型的别名,但是反过来就不行
- 定义引用如
int &f;
和int &f=10;
这两个都会报错,因为定义引用要马上初始化,别名也必须是变量,除非是const类型。
::作用域解析
下面讲到的vector的循环中会用到::
"::“在C++中表示作用域,和所属关系。”::"是运算符中等级最高的,它分为三种,分别如下
- 作用域符号
作用域符号”::“的前面一般是类名称,后面一般是该类的成员名称。
例如:A,B表示两个类,在A,B中都有成员member。
那么:
1、A::member就表示类A中的成员member。
2、B::member就表示类B中的成员member。 - 全局作用域符号
当全局变量与局部变量重复时可以用来区分,例如
#include<iostream>
using namespace std;
int a=1;
int main() {
int a=0;
printf("%d \n",::a);
printf("%d \n",a);
system("pause");
return 0;
}
}
输出为1和0。
- 作用域分解运算符
::是C++里的作用域分解运算符,“比如声明了一个类A,类A里声明了一个成员函数int b(),但没有在类的声明里给出f的定义,那么在类外定义b时,就要写成int A::b(),表示这个f()函数是类A的成员函数,例如:
class A {
public:
int b();
};
int A::b() {
}
vector向量
vector可以理解为动态数组
vector类型.cpp
#include<iostream>
#include<vector>//vector类型的头文件
//vector可以理解为动态数组
#include<string>
using namespace std;
class Dog
{
//
};
int main(){
vector<int> ival;//保存int类型的一个向量
vector<double> idouble;
vector<string> istring;
vector<Dog> idog;
vector<int> a;//a是一个空的容器
vector<int> b(10,2);//b容器中有10个2
vector<int> cc(10);//c容器里有10个0
a.push_back(1);
a.push_back(2);
a.push_back(3);
b.push_back(10);
b.push_back(11);
cout<<a.size()<<endl;
cout<<b.size()<<endl;
cout<<b[0]<<endl;
for(vector<int>::size_type bb=0;bb!=a.size();bb++)//vector<type>也有自己的类型
{
break;
}
//**********************
vector<int> v1;
v1.push_back(10);
v1.push_back(11);
vector<int> v2(v1);//将v1传入v2中,要同类型
//vector<string> v3(v1);这样是错误的
//***************************
//向量的下标元素不添加元素
vector<int> dd;//空的,所以没有下标
//推荐
int k;
cin>>k;
dd.push_back(k);
system("pause");
return 0;
}
vector的语法:
vector();
vector( size_type num, const TYPE &val );
vector( const vector &from );
vector( input_iterator start, input_iterator end );
C++ Vectors可以使用以下任意一种参数方式构造:
无参数 - 构造一个空的vector,
数量(num)和值(val) - 构造一个初始放入num个值为val的元素的Vector
vector(from) - 构造一个与vector from 相同的vector
迭代器(start)和迭代器(end) - 构造一个初始值为[start,end)区间元素的Vector(注:半开区间).
举例,下面这个实例构造了一个包含5个值为42的元素的Vector
vector v1( 5, 42 );
void ==push_back==( const TYPE &val );
push_back()
添加值为val的元素到当前vector末尾- vector也有自己的类型,
size_type
vector<int> v2(v1);
//将v1传入v2中,要同类型,非同类型会报错
using 名称空间
using 名称空间.cpp
#include<iostream>
using std::cout;
using std::cin;//cin就可以直接使用了
int main(){
//::叫做作用域操作符,前面就是名称空间
std::cout<<"hello world"<<std::endl;
system("pause");
return 0;
}
- 自己看一下代码吧,没啥好说的
指针和数组
指针和数组.cpp
#include<iostream>
#include<vector>
using namespace std;
int main(){
system("color 0a");
int ia[]={1,2,4,6,8};
int* ip=ia;//ia就是&ia[0]
cout<<*ia<<" and "<<ia<<" and "<<&ia[0]<<endl;
ip=ia;
int* ip2=ip+4;//等于ia+4;
cout<<*ip2<<" and "<<ip2<<" and "<<&ia[4]<<" and "<<&ia[0]+4<<endl;
//***************************
int* ip4=ia+10;//超过了,很危险,但是不会报错,取的是其他的数据
cout<<*ip4<<endl;
//*****************************
ptrdiff_t n=ip2-ip;//表示的是两个地址之间的距离,类型为ptrdiff_t
cout<<"距离为:"<<n<<endl;
//*****************************
int* p=ia+1;
int j=p[2];//p=&ia[0],p[2]=p+2=&ia[0]+2
int k=p[-1];
//方框的下标为指针的加法,负数也可以
cout<<j<<endl;
cout<<k<<endl;
//**************************
const size_t arr_size=5;
int arr[arr_size]={1,2,3,4,5};
int* ar=arr;//首指针
int* br=arr+arr_size;//超出末端的指针,即指向最后元素的下一个,c++允许这样做
//主要是为了写循环,与向量循环很相似
cout<<"指针循环"<<endl;
for(int* ptt=arr;ptt!=br;ptt++){
cout<<*ptt<<endl;
}
//相似的向量循环
vector<int> aa;
aa.push_back(1);
aa.push_back(2);
aa.push_back(3);
aa.push_back(4);
aa.push_back(5);
cout<<"向量循环"<<endl;
for(vector<int>::iterator nn=aa.begin();nn!=aa.end();nn++){
//aa.begin()指的就是aa向量里的第一个元素
//aa。end()指的就是aa向量里的最后一个元素的下一个
cout<<*nn<<endl;//迭代器也需要*来解引用
}
system("pause");
return 0;
}
- 数组名称就是数组首元素的地址
int ia[]={1,2,3}; //ia=&ia[0]
- 我个人推荐指针这样写
int* a
而不是int *a
,这两个是相同的,但是前者更容易理解,后者新手学习容易误会。 - 指针加法:&ia[0]+4就是&ia[4],这是用地址来写,可以以指针的方式来写,如:int* ip=&ia; 再ip+4;
- 但是如果ip后的值加的很大,这很危险,编辑器不会报错,但是取的是其他的地方的数据。
- 以上述的代码为基础
ptrdiff_t n=ip2-ip;//表示的是两个地址之间的距离,类型为ptrdiff_t
,ptrdiff_t可以用int来代替,但是逼格高呀。
int* p=ia+1;
int j=p[2];//p=&ia[0],p[2]=p+2=&ia[0]+2
int k=p[-1];
对于这里,指针也可以使用下标运算,表示的是加法,负数即为减法,表示的意义即为上面的指针加法。
7.指针循环与向量循环
const size_t arr_size=5;
int arr[arr_size]={1,2,3,4,5};
int* ar=arr;//首指针
int* br=arr+arr_size;//超出末端的指针,即指向最后元素的下一个,c++允许这样做
//主要是为了写循环,与向量循环很相似
cout<<"指针循环"<<endl;
for(int* ptt=arr;ptt!=br;ptt++){
cout<<*ptt<<endl;
}
//相似的向量循环
vector<int> aa;
aa.push_back(1);
aa.push_back(2);
aa.push_back(3);
aa.push_back(4);
aa.push_back(5);
cout<<"向量循环"<<endl;
for(vector<int>::iterator nn=aa.begin();nn!=aa.end();nn++){
//aa.begin()指的就是aa向量里的第一个元素
//aa。end()指的就是aa向量里的最后一个元素的下一个
cout<<*nn<<endl;//迭代器也需要*来解引用
}
先看看这个
int* br=arr+arr_size;//超出末端的指针,即指向最后元素的下一个,c++允许这样做
//主要是为了写循环,与向量循环很相似
这是一个超出末端的指针,指向最后元素的下一个,c++允许这个样子
for(vector<int>::iterator nn=aa.begin();nn!=aa.end();nn++){
//aa.begin()指的就是aa向量里的第一个元素
//aa。end()指的就是aa向量里的最后一个元素的下一个
cout<<*nn<<endl;//迭代器也需要*来解引用
}
这里用到的是迭代器。迭代器的begin()
与end()
与上面的指针循环很像,begin()
指的就是向量的第一个元素,而end()
指的是最后一个的下一个元素。
来说说迭代器
先说说为什么我不把迭代器放到指针与数组前,因为我懒!
迭代器.cpp
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> val(10,8);
vector<int> val2(10,9);
// begin()返回一个迭代器
// 指向第一个元素
vector<int>::iterator it=val.begin();
vector<int>::iterator it2=val2.begin();
//常迭代器
vector<int>::const_iterator it3=val.begin();//不能修改
while (it2!=val2.end)
{
*it2=11;
it2++;
}
//迭代器实际就是指针
*it=9;
cout<<val[0]<<val[1]<<endl;
*it++;
cout<<val[1]<<endl<<"后面的"<<endl;
//迭代器用来替代下标
for(vector<int>::iterator itt=val.begin();itt!=val.end();itt++){
cout<<*itt<<endl;
}
system("pause");
return 0;
}
- 来看看迭代器的API
C++ Iterators(迭代器)
迭代器可被用来访问一个容器类的所包函的全部元素,其行为像一个指针。举一个例子,你可用一个迭代器来实现对vector容器中所含元素的遍历。有这么几种迭代器如下:
迭代器 | 描述 |
---|---|
input_iterator | 提供读功能的向前移动迭代器,它们可被进行增加(++),比较与解引用(*)。 |
output_iterator | 提供写功能的向前移动迭代器,它们可被进行增加(++),比较与解引用(*)。 |
forward_iterator | 可向前移动的,同时具有读写功能的迭代器。同时具有input和output迭代器的功能,并可对迭代器的值进行储存。 |
bidirectional_iterator | 双向迭代器,同时提供读写功能,同forward迭代器,但可用来进行增加(++)或减少(–)操作。 |
random_iterator | 随机迭代器,提供随机读写功能.是功能最强大的迭代器, 具有双向迭代器的全部功能,同时实现指针般的算术与比较运算。 |
reverse_iterator | 如同随机迭代器或双向迭代器,但其移动是反向的。(Either a random iterator or a bidirectional iterator that moves in reverse direction.)(我不太理解它的行为) |
第种容器类都联系于一种类型的迭代器。第个STL算法的实现使用某一类型的迭代器。举个例子,vector容器类就有一个random-access随机迭代器,这也意味着其可以使用随机读写的算法。既然随机迭代器具有全部其它迭代器的特性,这也就是说为其它迭代器设计的算法也可被用在vector容器上。
如下代码对vector容器对象生成和使用了迭代器:
vector the_vector;
vector::iterator the_iterator;
for( int i=0; i < 10; i++ )
the_vector.push_back(i);
int total = 0;
the_iterator = the_vector.begin();
while( the_iterator != the_vector.end() ) {
total += *the_iterator;
the_iterator++;
}
cout << “Total=” << total << endl;
提示:通过对一个迭代器的解引用操作(*),可以访问到容器所包含的元素。
begin()
与end()
前面已经讲过了,不讲了- 常迭代器
vector<int>::const_iterator it3=val.begin();
,顾名思义,它不能修改。 - 迭代器与指针十分相像。
typeder
typeder.cpp
#include<iostream>
typedef int 整形;
typedef float 浮点型;
using namespace std;
int main(){
int c;
整形 a;
浮点型 b;
system("pause");
return 0;
}
没啥好讲的,自己看吧
C++标准库string(1)
C++标准库string(1).cpp
#include<iostream>
#include<string>
using namespace std;
int main(){
char a[10];//c语言的做法
string aa;//c++的做法
//string的4中初始化方法
string s1;
string s2("hello world");
string s3(s2);
string s4(10,'hse');
cout<<"第一种"<<s1<<endl;
cout<<"第二种"<<s2<<endl;
cout<<"第三种"<<s3<<endl;
cout<<"第四种"<<s4<<endl;//只会输出eeeeee,输出最后一个
string s5="hahha";//这是一种不好的方法,原理是s5先被默认初始化然后再复制,效率低
cout<<"hello wprld"<<endl;//这个是字符数组类型
cout<<s2<<endl;//s2是string类型
system("pause");
return 0;
}
- C++的string与C语言的char数组不一样,string功能更加强大,并且更加好用。
- 来看看C++ API吧
语法:
string();
string( size_type length, char ch );
string( const char *str );
string( const char *str, size_type length );
string( string &str, size_type index, size_type length );
string( input_iterator start, input_iterator end );
字符串的构造函数创建一个新字符串,包括:
以length为长度的ch的拷贝(即length个ch)
以str为初值 (长度任意),
以index为索引开始的子串,长度为length, 或者
以从start到end的元素为初值.
例如,
string str1( 5, 'c' );
string str2( "Now is the time..." );
string str3( str2, 11, 4 );
cout << str1 << endl;
cout << str2 << endl;
cout << str3 << endl;
显示
ccccc
Now is the time...
time
即为代码中的四种初始化方法
- 而对于
string s5="hahha";
这是一种不好的方法,因为他的原理是s5先被默认初始化然后赋值,效率低下。
C++标准库string(2)
C++标准库string(2).cpp
#include<iostream>
#include<string>
using namespace std;
int main(){
string s1("hello world");
string::size_type a =s1.size();//size_type类型专门保存字符串大小,但是要用string在前面,using无效
cout<<"字符个数(字符串大小)"<<s1.size()<<endl;//空格也算
//判断空字符串
string b;
if(b.size()==0){
cout<<"这是一个空字符串"<<endl;
}
if(b.empty()){
cout<<"这是空字符串"<<endl;
}
//***********************************************
string ss1("张飞");
string ss2("刘备");
if(ss1>ss2){
cout<<"张飞比刘备大"<<endl;
}
//********************************************
//字符串相加
string sss1("hello ");
string sss2("world\n");
string sss3=sss1+sss2;
//string sss4="hello"+"world";有些语言可以这样写,但c++不允许
string sss6=sss1+"hello"+"world";/*里面只要有一个string的值就可以用加号连接
因为" "是char类型*/
cout<<sss3;
system("pause");
return 0;
}
string::size_type a =s1.size();
size_type类型专门保存字符串大小,但是要用string在前面,using无效,也可以用int类型替代。更容易让人理解,主要还是逼格高。- size语法:
size_type size();
size()函数返回字符串中现在拥有的字符数。空格也算 - empty语法:
bool empty();
如果字符串为空则empty()返回真(true),否则返回假(false). - string的拼接
string sss1("hello ");
string sss2("world\n");
string sss3=sss1+sss2;
//string sss4="hello"+"world";有些语言可以这样写,但c++不允许
string sss6=sss1+"hello"+"world";/*里面只要有一个string的值就可以用加号连接
因为" "是char类型*/
这样可以将两个string类型的字符串拼接在一起,但是拼接的时候,其中一定要有一个string类型才可以,否则报错
C++标准库string(3)
C++标准库string(3).cpp
#include<iostream>
#include<string>
//#include<ctype.h>c++中不提倡
#include<cctype>//提倡这样的写法
using namespace std;
int main(){
//string不是c语言中的字符数组
string a("hello world");
cout<<a[0]<<endl;
//********************************
//标点符号的个数
string a2("hello world!!!");
string::size_type b=0;
for(string::size_type index=0;index!=a2.size();index++){
if (ispunct(a2[index]))
{
b++;
}
}
cout<<"标点符号共有:"<<b<<endl;
//isalnum()如果参数是数字或字母字符,函数返回非零值,否则返回零值。
//isalpha()如果参数是字母字符,函数返回非零值,否则返回零值。
//iscntrl()如果参数是控制字符(0和0x1F之间的字符,或者等于0x7F)函数返回非零值,否则返回零值
//isdigit()如果参数是0到9之间的数字字符,函数返回非零值,否则返回零值.
//isgraph()如果参数是除空格外的可打印字符(可见的字符),函数返回非零值,否则返回零值
//islower()如果参数是小写字母字符,函数返回非零值,否则返回零值。
//isprint()如果参数是可打印字符(包括空格),函数返回非零值,否则返回零值。
//ispunct()如果参数是除字母,数字和空格外可打印字符,函数返回非零值,否则返回零值。
//isspace()如果参数是空格类字符(即:单空格,制表符,垂直制表符,满页符,回车符,新行符),函数返回非零值,否则返回零值。
//isupper()如果参数是大写字母字符,函数返回非零值,否则返回零值。
//isxdigit()如果参数是十六进制数字字符(即:A-F, a-f, 0-9),函数返回非零值,否则返回零值。
system("pause");
return 0;
}
- C++的头文件中不建议使用含有.h的头文件
- 关于这些string的函数也可以去查阅API等等,代码列出的很全了。
cin等常用详解
本节包括cin,cin.get,cin.getline,getline等常用的用法
cin详解.cpp
#include<iostream>
#include<string>
using namespace std;
void clean() {
while (getchar()!='\n')
{
continue;
} //清理STDIN,这个方法很棒
}
int main() {
char ch;
char ab[20];
int a, b;
cin >> a >> b;
cout << a+b<<endl;
cin >> noskipws;//设置cin读取空白符
while(cin >> ch)
cout << ch;
cin >> skipws;//将cin恢复到默认状态,从而丢弃空白符*/
clean();
cin >> ab;
cout << "遇到空格结束:"<<ab << endl;//遇到空格就结束了
clean();
char ch2[100];
ch = cin.get();
cout << "输出ch"<<ch << endl;//单个字符可以这样写
cin.get(ch2,20);
cout <<"输出ch2"<< ch2 << endl;//只会输出19个字符加上一个结尾\0
//cin.getline用法
//cin.getline有三个参数
char m[100];
cin.getline(m,20,'a');//最后一个默认为\0,即遇到什么字符开始截断
cout << "输出m"<<m << endl;
string cc;
getline(cin,cc);
cout << "输出的cc为" << cc << endl;
//和cin.getline()类似,但是cin.getline()属于istream流,
//而getline()属于string流,是不一样的两个函数
system("pause");
return 0;
}
- cin的>> 是会过滤掉不可见字符(如 空格 回车,TAB 等)
- 若想不过滤掉的话可以使用
noskipws
,然后再用skipws
关闭 cin.get();
有两个参数,第一个是要输入到的字符组,第二个是输入的长度cin.getline();
有三个参数,前两个与cin.get();
相同,最后一个为设置截至符,默认为\0。getline();
和cin.getline()
类似,但是cin.getline()
属于istream
流,而getline()
属于string
流,是不一样的两个函数
技巧讲解之清空标准输入流(STDIN)
void clean() {
while (getchar()!='\n')
{
continue;
} //清理STDIN,这个方法很棒
}
- 用getchar()重复读取输入流中的值来浪费他们去做判断,从而将这些不想要的值给"舍弃"掉
C++标准库bitset
C++标准库bitset.cpp
#include<iostream>
#include<bitset>
#include<string>
using namespace std;
int main(){
bitset<32> a;//a的大小为32位2进制,全部都是0
cout<<a<<endl;
bitset<16> b(0xffff);
cout<<b<<endl;
string str("1101");
bitset<32> e(str,str.size()-4);
cout<<e<<endl;
bitset<32> f(156);
cout<<f<<endl;
bool g=f.any();//any()函数返回真如果有位被设置为1,否则返回假。
bool h=f.none();//none()返回真如果没有位被设为1,否则返回假。
int i=f.count();//count()函数bitset中被设置成1的位的个数。
//***************************
bitset<32> j;
j[5]=1;
cout<<j<<endl;//从后往前数
for(int index=0;index!=9;index++){
j.set(index);//set就是将某一位变成1
//而set()是将每一位都设置成1
j.reset(index);//reset是将其变成0
j.flip(index);//翻转,将其0变成1,1变成0
}
unsigned long long bb=j.to_ullong();//to_ulong()返回bitset的无符号长整数形式。就是将其转换成10进制数
bitset<8> gg;
cin>>gg;
bitset<8> n(gg);//用其他的bitset初始化新的bitset
bitset<8> nn(14);
cout<<(gg & nn)<<endl;//位与,这些操作优先级比较低,要用括号括起来
cout<<(gg|nn)<<endl;//位或
system("pause");
return 0;
}
- 注意头文件
#include<bitset>
这个可不能忘 - 来看看bitset的语法格式
语法:
bitset();
bitset( unsigned long val );
C++ Bitsets能以无参的形式创建,或者提供一个长无符号整数,它将被转化为二进制,然后插入到bitset中。当创建bitset时,模板中提供的数字决定bitset有多长。
例如,以下代码创建两个bitsets,然后显示它们:
// 创建一个8位长的bitset
bitset<8> bs;
// 显示这个bitset
for( int i = (int) bs.size(); i >= 0; i-- ) {
cout << bs[i] << " ";
}
cout << endl;
// 创建另一个bitset
bitset<8> bs2( (long) 131 );
// 显示
for( int i = (int) bs2.size(); i >= 0; i-- ) {
cout << bs2[i] << " ";
cout << endl;
}