C++初级 入门笔记学习(一)

1,C++Primer初级:
预处理(E查看)->编译(S查看)->连接

13_枚举:可以尽可能多用枚举,多个const变量,可以用枚举去做;

string name("aaa"); //定义,初始化
string::size_type size = name.size();  //字符个数,保证可以获得对应的大小,int size = name.size(); 这个不安全
name.empty() 判断是否为空
字符串相加,是表示将两个连接起来,但是两个不能都是字符串字面值
例如 name+name2,但是不能“aa”+“bb”
ispunct(name[i])判断某个字符是否是标点符号(头文件cctype)
字符串的输入,不能用cin要用getline,然后用for循环,检查输入的字符串的每一个字符

vector 动态数组,其实是个类类型,一般可以替换到普通的数组。
常用里面的empty(),vector<int>::size_type, v.size(),和v.push_back();
vector begin(),返回一个迭代器,vector<int>::iterator iter = v.begin; 迭代器实质上是一个指针,所以可以给该值赋值*iter=9;
一般用迭代器替代下标,例如用vector<int>::size_type idx=0;
可以for(vector<int>::iterator i=v.begin();i!=v.end();i++){
    cout <<"迭代器 "<<*i<<endl;//这样可以通过*i获得vector v中的元素;即迭代器是个指针,这比下标取元素简单多了,尽量多使用迭代器方式,少用下标形式取元素
}
常迭代器与非常迭代器,区别为,不可以与可以通过迭代你修改数据
例如 vector<int>::const_iterator i = v.begin(); 常迭代器常用来读取数值,不用常迭代器修改数据;

bitset<32> a(156) 将156按二进制存储,到32位大小空间中。
a.any(); 判断是否有1,a.none()判断没有一个1,a.count() a有几个1.a.size()获得a的大小;a.set(index) 设置某位为1。或者直接下标写法,a[index]; a.set() 设置所有的为1. reset是变为0;

void*指针,是万能指针,可以指向任意的类型。但是这种指针不能通过其操作对象。
常指针才能指向常对象;普通指针不行,常指针必须初始化。常指针(常用来传递参数,防止修改参数)可以指向非常对象,这样之后,不能通过指针来修改其对象的值,也不能修改其指向再指向其他的对象。
举例:
#typedef string *pstring;
const pstring cpst; 与string *const cpst1;等同(常指针,指向字符串,必须初始化),与const string *cpst2;不同,指向常字符串指针,不用必须初始化
int a[]={1, 2, 3, 6, 5, 4}
int *ip1=a; //指针可以可以看做数组的迭代器。
int *ip2 = a+4;
ptrdiff_t a=ip2-ip1;得到地址差。即a=4;相差四个数

引用声明时,必须初始化。指针可以不用

c风格的字符串,必须用\0结束。
char a1[] = {'a', 'b', '\0'};是
char a2[] = "ab";是
char a3[] = {'a', 'b'} 不是,只是c风格的字符数组
const char *a4 = "ab";是
strlen(a2);计算结果不包括\0
c风格的字符比较,用strcmp;而c++风格的字符串string类型,可以直接比较。字符串string类型可以s1>s2,s1==s2。但是c风格的字符不能a1==a2等操作,这样操作的仅仅是地址。而不是内部的值;
c风格的字符不能用=做拷贝,必须用strcpy(a1,a2);。strcat(a1,a2);c风格的字符串连接,c++的string类型的连接直接用+号,
标准库中strcmp,strcpy,strcat等都比较危险。一般使用带n的方法,例如strncmp,strncpy,strncat等,strncpy(a1,a2,2);n制定了拷贝的大小。不然很多黑客可以利用这些漏洞,可以修改里面的东西(缓冲区溢出攻击)。尽可能使用c++风格的字符串string类型。

动态数组创建,c语言,malloc(n*sizeof(int)),free(a)。c++ new int[n]和delete[] a;
int *a = new int[10];没有初始化。int *a = new int[10]();初始化了
string *astr = new string[10];使用默认的构造函数进行初始化;
数组等的循环,一般用指针,专业点,可以认为指针是数组的迭代器。
int *ai = new int[10];
for(int *p= ai; p!=ai+10; ++p){
   *p=11;//通常通过指针作为迭代器,给数组ai循环初始化操作;
}

c与c++转换
string 和char相互转换:string类型用c_str()转换后赋值的时候,对象必须是const,const char *= st.c_str();
数组和vector转换:int a[6]={1,2,3,4,5,6}; vector<int> v(a, a+6); 复制的时候,后面的是不包括的,所以是a+6,而不是a+5,或者用迭代器vector<int>::const_iterator 
vector<int> ivec;
int ival; //tmp变量,很重要
while(cin>>ival){ivec.push_back(ival) }//input data to vector; 创建动态数组 int *a = new int[ivec.size()];动态创建一个数组
字符串数组的c实现为:
vector<string> svec;
string sval;
while(cin>>sval) svec.push_back(sval);
char **c = new char*[svec.size()]; //赋值时,注意最后一个是\0
赋值可用中间变量char *tmp = new char[svec.size()];
然后将c[i]=tmp; //因为c是二重指针,所以存放的是地址;释放的时候也得用for循环。

多重数组
int a[n1][n2]; int (*ip)[4];//注ip是一个指针
常常用typedef简化指向数组的指针,例如:#typedef int int_array[4];然后,写int_array *ip;与以上的ip一样;这样可以避免丢失括号而导致ip不是指针的问题,ip=a;表示指向a的第一行,即ip是一个指针,所以使用ip取a中值时,还得使用一个指针;
int *ip[4]; //ip是一个数组,区分以上的有括号的情况

算术运算符
double 10/3; 会等于3,除号是整除和小数除
cout<<-21/-8;会等于-5,如果一正一负,结果可能是正也可能是负 -21/8 c++中是不确定的

箭头操作符与指针
Dog *p;
p=new Dog();
p->foo(); 和(*p).foo();等同,其中,foo为类Dog的方法
指针的指针,一般用一个临时指针,将中间值赋值给它;
vector<string*> spvec;
string str;
while(cin>>str)
{ string *pstr = new string;//相当于临时指针
  *pstr=str;
spvec.push_back(pstr);
}
这里一定要释放
vector<*string>::iter = spvec.begin();
while(iter != spvec.end()){ delet e * iter;iter++;}  //一般不直接操作spvec,而是通过迭代器,操作。

条件操作符,一般if 类型的可以用这个替换(a>b)?a:b; 不过慎用,避免嵌套,看起来繁杂不好理解

new delete
new的时候,如果是内置类型,一般要用括号;
例如int *a = new int();调用默认构造函数初始化
如果int *a = new int; 如果是内置类型,则没有初始化,很危险。
如果是在类类型,在main中创建的,这是等同的;所有的new 一定要使用delete撤销

强制类型转换符
C++新式的static_cast dynamic_cast reinterpret_cast const_cast 和传统C风格的;
举例:double dPi=3.1415926; int nNum=static_cast<int>(dPi);//这种啰嗦的写法提醒作者注意,这里用了强制转换,其实是和C风格一样
避免使用强制类型转换,C++一般不提倡c风格的在转换,因为没有检查,而dynamic_cast等,如果转换成果,返回值不为空,如果为空,表示转换不成果,例如:int n = dynamic_cast<int>(val);可以使用if(n) 判断是否成功

文件操作,抛出异常try catch,throw相结合的方式
FILE *input=fopen("a.txt", "rb");//常常对fopen进行判断,是否打开成功
FILE *output=fopen("b.txt", "rb");
一定要记得关闭fclose(input);fclose(output);
int size = fread(dst, sizeof(float), num, input);//size为读取的实质大小,dst为目标存储位置,sizeof(float)每次读多大的大小,num是总共读多少,input为输入文件流
int size_out = fwrite(dst, sizeof(float), num, output);//这里
用if(input==NULL) throw 1;//抛出异常,可以输出,数字,字符串,类对象。
代替if(input==NULL) return 1;
int func(){FILE *input =fopen("a.txt","rb");if (input==NULL) throw 1;}然后用以下部分处理异常;
try{func();}catch(int e){printf("e:%d",e);} catch(const char *e){}

标准异常类:exception,runtime_error rang_error,invalid_argument在stdexcept中),bad_alloc在头文件<new>中,
例如:try{}catch(bad_alloc err){cout<<"allocate exception";}
int function(int a){if(a<0)throw out_of_range("data not lower zero");}//定义自己的异常程序。调用的时候,用try catch即可。
设计自己的异常类,可以在自己定义的类中,添加类成员,这个类成员一般可以继承已有的异常类,然后实现里面的what方法:virtual const char *what() const throw(){ return "your message";}
602653
使用预处理进行调试,
第一使用#ifndef NDEBUG 方式,二,assert(头文件cassert)
获取对应的信息,三,使用__FILE__ __LINE__ __DATE__ __TIME__ 直接和内部定义的宏一样返回对应的文件位置,行数,日期时间等,直接使用。assert(3==(1+2)),如果不相等,则返回详细错误信息。其中NDEBUG为内部定义的宏,不使用调试的意思,如果用了,assert也将不起作用

引用形参;目的:一为了修改值,能传递回来,第二,防止赋值,降低操作时间,第三,使用const,避免修改
function(string a); 如果传递进去的a过长a="aaaaaaaaaaaaaaaaaaaaaa";会导致效率低下,特别是对a还不做修改时,强烈建议使用const 引用。function(const string &a);
指针引用 function(int *&val)val是个引用类型,同时也是指针。
例如:定义交换指针函数 swap(int *&a, int *&b); int a=9; int b=8; int *pa=&a; int *pb=&b;
swap(pa, pb);最后pa指向了b,pb指向了a。

vector list deque作为形参的时候,一般不是直接传递进入的,而是使用迭代器作为形参
例如:function(vector<int>::iterator begin, vector<int>::iterator end);

返回值为引用或者指针的时候,一定要注意,不要反悔一个函数结束时,此变量就不存在的对象:例如:int * function(){ int a = 0return a;}//这是很危险的,因为函数结束的时候,a就不存在了。切记。常引用或指针,不能赋值给非常引用或指针;
举例:改变字符串中的某个字符
char & change_s(string &str, int i){return str[i];}
调用时 string strv("aaa"); char &c=change_s(str,1); c="i"; //结果str变为了aia;因为这里传递进去的是引用,返回的也是引用,多以对原来的变量进行了修改,也是对原来的引用变量修改。而不是赋值操作,一定要区别,并且返回为引用指针的时候,一定要小心; 

大量的递归调用会占用内存和时间。默认实参,一般在函数声明处写。

内联函数一般放在头文件中,节省时间开销,递归和大函数,避免使用内联函数,内联函数在编译的时候,会展开。可以理解为避免了调用操作,而是直接展开替代。内联函数的编译对编译器来说,必须是可见的,所以一般得放在头文件中。

class
要变成对象了,才能使用,在类的定义中,没有对象,一般用this(类似于参数)表示,也可以不写,写上更好,表示这个变量时当前对象的。
例如
class Sale{
private:
bool val;
publicbool function(const Sale sa){
    this->val = sa.val; //this表示这里Sale的对象,不是这里的sa
}
}
只有静态成员才能直接使用初始化,一般的变量只能通过构造函数进行初始化,初始化的时候,一般用初始化列表形式进行初始化例如上面的用Sale():val(true),val1(0){};多个变量可以使用""号隔开。初始化过程中,一般基本的数据类型都必须初始化,一些有默认进行初始化的类型,不用进行初始化,例如string类型,就可以不用初始化,会使用默认的初始值进行初始化

重载与作用域:
重载函数一定要写到一个作用域。防止函数被屏蔽或隐藏现象。
变量名和函数名同名,可能导致函数屏蔽:例如 int init = 0; int a = init();其中init()为函数,这样会导致函数init();函数不可见,会出错,这种现象叫函数隐藏或者屏蔽。function(int *const a);和function(int *a);不能构成重载,但是function(int *a); 和function(const int *a);构成重载;

指向函数的指针(函数指针变量)(一般用typedef简化其声明)
bool (*pf)(int a, int b);声明了一个指向函数的指针,表示指向函数类型为参数表为(int, int)返回值为bool类型的函数指针,这里的(*pf)不能去掉。调用的时候,就是
pf=functionname;或者pf=&functionname 然后直接使用pf(3,4);或者(*pf)(3,4);.和调用functionname(3,4)一样。因为使用函数指针的时候,写起来太复杂。所以一般使用typedef 简化。
例如:typedef bool (*dPf)(int a, int b); 在使用的时候,就直接使用dPf pf;声明函数指针变量,就像直接使用bool (*pf)(int a, int b);一样,并且可以方便的定义多个函数指针变量,dPf pf1, pf2;非常方便。函数指针因为是个变量,所以可以作为函数形参,例如一个函数调用另外一个函数的时候,可以将另外一个函数的指针传递进入,实现一个函数调用另外一个函数。函数指针的形参必须和指向的函数的参数类型精确匹配。

流对象是不能复制的,所以要传递流对象的时候,一般用引用类型或者指针。
int function(ofstream ofs); ofstream of; 使用的时候function(of)会失败,因为流对象不能复制。把函数改为int function(ofstream &ofs)后,则可以这么使用了。

流的条件状态。if(cin.bad()) cin.fail() cin.eof() cin.good() cin.ignore(200, "\n"),要么忽略前两百个,要么忽略到\n. cin.setstate(XXX), cin.rdstate()等等
如果输入int a; cin>>a,你输入的str,check这个流的状态的时候,可以知道cin.fail()为真,也就是这个输入是失败的。cin.clear()流状态清理到正常的 状态
流对象的字符串为C风格的字符串,例如ofstream f; f.open(str.c_str())
 
逗号运算符,是逗号后面的为条件。例如while(cin>>a, !cin.eof()),其实while(cin>>a)当eof fail bad的时候会结束输入。而while(cin>>a, !cin.eof())只有到流的末尾时,才结束。另外注意,文件流的状态不会因为close了,而跟新,所以连续的用某个流open文件的时候,一定要记得用clear来恢复流的状态,然后再使用。
ifstream fs("file.txt"); 或者 ifstream fs;fs.open("file.txt");两种方式,将ifstream绑定文件。

文件模式:
in,out,app后面添加,ate定位到文件最后的文件定位指针,trunc截断,覆盖,binary

字符串输入输出流 stringstream istringstream ostringstream 是内存操作;cout cin 显示在屏幕,不是内存操作。

 

posted on 2016-02-25 00:02  Sanny.Liu-CV&&ML  阅读(369)  评论(0编辑  收藏  举报

导航