C++重要知识点精华总结
cin的使用:
1>cin>>a;键盘读入数据赋值给a;
1>程序的输入都建有一个缓冲区,即输入缓冲区。一次输入过程是这样的,当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin函数直接从输入缓冲区中取数据。
2>当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功(第一个字符不是是空格tab或换行),字符后面的分隔符是残留在缓冲区的,cin>>不做处理。
3>cin.peek()的返回值是一个char型的字符,其返回值是指针指向的缓冲区的当前字符。但它只是观测指针停留在当前位置并不后移;
4>cin.get()用来从指定的输入流中提取一个字符(包括空白字符),函数的返回值就是读入的字符。提取完后指针会后移。
综上:结束while(cin>>i)的方法是
while(cin.peek()==’ ’){cin.get();}
if(cin.peek()==’ ’){break;}//按回车键就会终止输入。
1>用到C++中标准输入输出、string的文件都要加上using namespace std;
关于类的总结:
1、当派生类中出现了基类的同名覆盖时若想引用基类的成员,应当加作用域限定符,如课本P384的area()函数。否则默认使用派生类的成员。
2、类默认情况下其成员是私有的。
3、类的对象只能通过成员运算符“.”引用public类型的成员,而不能通过->来引用。
4、注意this指针是指向对象的,它代表对象的首地址。
5、注意:私有成员只能被本类的成员函数访问,不能在类外(不在类体与类体(class{};范围)里的成员函数里称为内外)被访问,保护成员只能被本类的成员函数和派生类的成员函数访问(通过对象访问也不可以), 不能在类外被访问(通过对象访问也不可以), 而共有成员可以在类内与类外被访问。派生类的对象是可以直接访问基类的共有成员的,但是不能访问基类的保护成员,派生类会接受基类中除了构造函数和析构函数外所有成员。
6、如果两个类之间没有继承与派生的关系,而类B又想引用类A中的成员只有两种方式可以实现:①加入定义类A的头文件,然后定义类A的对象,通过类A的对象来引用。②加入定义类A的头文件,通过类名与作用域限定符来引用,如A::getchar()
7、定义类时在类体中定义的其它类的对象,该对象就等价于本类的一个成员,能被本类和本类的派生类的对象引用。
8、对于类的成员该定义为公有的还是还是私有的的回答:如果目前只打算只给本类成员函数使用的话就定义私有的,若要打算给派生类使用的话就定义为保护的(一般题目中当我们事先写了基类或虚基类的时候才会去写protected)若觉得该变量可能在类外被使用则设为public。
黄师兄对此说法:有关类的数据成员全部定义为private(在类外要使用的话可通过定义类 的接口函数来实现),函数成员可定义为private或public,基类中的成员都是定义为protected,某成员要给派生类使用的话就要定义为protected.
9、本类的成员函数中是可以直接(不需要通过对象)使用本类的数据成员的。在其他函数中使用本类的公有成员话就需要通过对象来进行了。好像友元型也可以。
10、引用基类的成员有三种方式:1.通过对象. 2.通过对象的指针-> 3.通过类名::
问:什么情况下会选择通过类名呢?
答:当某一函数经过多次重载且不想通过对象或对象的指针时,通过类名加::程序才知道派生类中用的是哪一个重载函数。
11、类的数据成员虽然在定义的时候系统没有为它分配内存,但是本类的成员函数(即使在内体外定义也属于类内)还是可以直接引用它的,但是在类外(非类的成员函数)就需通过上一条总结的方法来引用。
12、有时某一派生类的不同的父类可能具有数据成员名或函数成员名一模一样的情况,这时为了避免编译器正确检索到自己想要的那一个类的成员,自己必须手动加上对应的命名空间。
13、作用域限定符 “::”可以用它声明函数时属于哪一个类的,作用域限定符一般用在数据成员或成员函数的前面,注意作用域限定符在多重继承中的灵活妙用。
14、用类的成员函数处理问题比用普通函数处理问题的好处在于:
1>要想通过调用普通函数改变某一变量的值必须通过指针、数组做参数或引用的方法实现,而通过成员函数的方法函数形参为空也可做到(相当于通过成员函数达到了引用的效果^^)。也就是类的数据成员有了全局变量的效果。另外在C++代码中如何某个函数里的局部变量的使用率很高的话,可在相应类中定义其数据成员,这样程序运行时系统不用多次分配内存。
项目心得:不能一味地想着用全局变量。特别是在多线程程序中全局变量会有较大的风险(某个全局变量的值在某一个地方被改变)。看容器总结部分第十条。
15、注意对象不可以通过成员运算符引用类中定义的private成员(即使是public成员也只能通过本类对象来访问)。private成员只能在类体内被引用。
16、记住课本图13.2,另外注意cin是istream的派生类的对象,而get函数和getline函数都是istream的公用成员函数,顾对象cin可引用继承过来的派生类成员get与getline,同理还有put函数也是ostream的成员函数,cout对象可引用它。
17、注意类的数据成员除了可以被该类的对象引用外,还可以被其成员函数引用。
18、在某一个类中引用另外一个类的非静态成员需在本类中定义那个类的对象来引用。
19、面向对象的特点:封装与信息隐蔽、继承与派生、多态性。
20、在一个类A中定义另外一个类B的对象,定义的时候系统是不会为类B的对象分配内存的(不会去执行类B的构造函数),但是当定义类A的对象的时候(会去执行类B的构造函数)就会为类B的对象吧分配内存。
关于C++定义的类什么时候定义对象要不要定义对象的回答:
1、一般我们定义的类基本都是会为其定义对象的。如果你的类里全部都是静态成员的话也可以不通过对象来引用类中的成员(通过类名::的方法)。对象可以在函数外面定义全局的对象,也可以在某一个类或某个函数里定义局部对象。一个类可以定义多个对象,可以通过static方法让一个类的多个同名对象为同一对象。
2、一个类A想要用另一个类B的成员,而不想定义类B的对象,可以通过在类A中定义类B的指针。
3、一个工程肯定是有一个唯一的对象的。像MFC框架那样,以该对象作为整个工程的主题骨架,可以在该对象中定义其他类的对象,从而可使用各个类中的各个成员。
有关this指针:
1、this指针的值是当前被调用的成员函数所在的对象的起始地址
问题:当前被调用的成员函数所在的对象怎么确定??
所以要学会判断当前是哪个对象调用该成员函数
答案://MFC程序的每一个函数,如OnBnClickedShow()只是一个成员函数,成员函数是要通过对应类的对象来调用的,MFC程序中,主对话框的对象是dlg,若是自己写的程序,则需要定义对应的对象。
虚函数的精髓:
1、虚基类一般用在多重派生中,使虚基类在间接派生类中只有一份成员。
2、注意虚函数的作用。在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们。
通过定义基类的指针来指向基类对象或基类的派生类对象从而去引用基类或其派生类的成员。(问:可以通过定义指向派生类的指针来指向基类从而引用基类成员吗?不可吧。。。)
注意:如果派生类中重定义了基类中的虚函数,即使用基类的类型指针调用该函数,那么实际上连编的是派生类的函数。(但是先把指针指向基类对象了的话,连编的是基类的函数)
然而,当派生类具有了新的特征,有些时候,我们仍然希望保留基类的特征,也就是说,我们希望能够调用基类定义的out函数,所以就出现了A::out()这样的语法,以便允许从派生类中直接调用基类实现的out虚函数。
编写程序过程中一般在基类和派生类有同名同参函数时才需要声明虚函数
纯虚函数:纯虚函数是一个在基类中说明的虚函数,它在基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本。
纯虚函数声明格式为:Virtual 函数类型 函数名(参数表)=0;
抽象类:
1.带有纯虚函数的类是抽象类。抽象类的主要作用是通过它为一个类族建立一个公共接口,使它们能够更好的实现多态性。而接口的完整实现,即纯虚函数的函数体,要由派生类自己定义。
2.如果派生类中没有给出全部虚函数的实现,这时派生类仍然是一个抽象类,抽象类是不能实例化的(不能声明一个抽象类的对象),但是可以声明抽象类的指针和引用,通过指针或引用就可以访问派生类对象,进而访问派生类成员(这种访问是具有多态性的)。
有关构造函数的总结:
1>程序中不写构造函数的话,就没有初始化步骤。
2>
以下①和②两构造函数都是默认构造函数,不可在一个类中同时出现二者。其中③不属于默认构造函数,当程序中只有③时,定义对象时必须同时给定3个实参。
①Box(){length=0;width=0; height=0;} 注意:后面部分不写会报错
②Box(int l=0,int w=0,int h=0):length(l), width(w), height(h){} //在声明构造函数时指定默认参数
③Box(int l,int w,int h):length(l), width(w), height(h){}
注意:在一个类中定义了全是默认参数的构造函数后,不能再定义重载构造函数,例如上面的②不能和其他任何构造函数共存(虽然默认的构造函数只有一个)。但①和③是可以共存的
推荐使用第②种方法,它相当于好几个构造函数,使用起来相当灵活方便。
4、注意定义对象数组时如何对数组中所有的对象元素初始化,以及如何引用数组元素。
5、定义指向对象的指针的形式为 Time *p=&t1;
定义指向对象数据成员的指针的形式为: int *p=&t1.time
定义指向对象成员函数的指针的形式为:int (Time::*p)()=&Time::get_time(); (t1.*p)();
不推荐使用,推荐使用指向对象的指针,通过指向对象的指针便可实现对所有成员的访问。
6、每次创建一个对象都会执行默认的构造函数,自己创建对象的同时可以调用重载的构造函数来初始化。
7、构造函数的主要用在对类的数据成员初始化。构造函数无函数类型,函数名即为类名。
8、用参数初始化表对数据成员初始化时可以直接在类体中定义构造函数。
9、所有函数的形参名可省略,但形参类型不可省。一个类只能有一个默认构造函数。定义了带有参数的构造函数在定义对象时一定要初始化(给实参)(问,有默认的和带参数的定义对象的时候不给实参会不会有错),注意也可同时定义一个不带参数构造函数作为默认的构造函数,这样便可灵活地对定义的对象初始化。
问题:程序如何确定哪一个才是默认的构造函数呢?解答:在声明时全部参数都指定了默认值的构造函数也属于默认构造函数。声明了无参的构造函数的话它便也是默认构造函数,注意一个类只能有一个默认的构造函数,否则是非法的。
10、对于用参数初始化表初始化构造函数记住是把实参的值传递给数据成员,如:Student(int n,string nam):num(n),name(nam){}
////等价于在构造函数的函数体内通过赋值语句定义构造函数,即用形参n的值初始化数据成员num,nam的值赋给nam
有关运算符重载:
1、如果运算符重载函数作为类成员函数时,它的第一个参数必须为本类的对象。因为此时可以通过this指针自由的访问本类的数据成员,而C++编译系统将程序中的表达式C1+C2解释为C1.operator+c2
2、运算符重载的方法:例如,想将“+”用于complex类(复数)的加法运算,函数的原型可以是这样的:
Complex operator +(complex &c1,complex &c2);
运算符重载函数可作为类的成员函数外,还可以是非成员函数(在有关类中把它声明为友元函数)。注意:当为成员函数用于双目运算符的重载时,重载函数的形参有一个参数是可以隐含的,如Complex operator +(complex &c1); 当作为非成员函数(在有关类中把它声明为友元函数)主要用在运算符左侧的操作数不属于该类类型,此时对于双目运算符的重载,重载函数的参数列表中必须有两个参数,不可省略。
注意对“<<”和“>>”重载的函数形式为:
istream & operator >>(istream &,自定义的类&);
ostream & operator >>(ostream &,自定义的类&);
注意只能将重载“<<”和“>>”的函数作为友元函数。
3、当程序中有运算符重载函数时记得重载函数的函数体最后一定有return语句,否则编译时会出现报错。
4、注意重载(同一层次上的同名函数的问题)与覆盖(不同层次上的同名函数的问题)的区别。
关于友元的总结:
1、友元可以访问与其有友好关系的类中的私有成员(但是并不能访问保护成员),友元包括友元函数与友元类。友元函数不仅可以是一般函数也可以是另一个类中的成员函数,声明方式分别为:
friend void display(Time &)
friend void Time::display(Date &)
注意:某类的友元函数并不是该类的成员函数,没有This指针,不能默认引用该类的成员,必须指定要访问的对象。
如 void display(Time&t) {cout<<t.hour<<t.minute<<t.sec<<endl;}
有关const的总结:
定义常对象的一般形式为:const Time t1(实参表) 或Time const t1(实参表)
定义常成员函数的一般形式为:void get_time()const
常数据成员的一般形式为: const int hour
指向对象的常指针的一般形式为:Time *const ptr1=&t1
指向常对象的指针变量的一般形式为:const Time * ptr
注意:关于常对象、变量、指针const一般在前,关于函数的const一般在后。常对象的数据成员都是常数据成员,但其成员函数不一定都是常成员函数。
9、对象的复制 Box b2(b1);用对象b1复制出对象b2
有关静态成员的总结:
1、如果希望各对象中的数据成员的值是一样的,就可以把它定义为静态数据成员(再为对象所分配的空间中不包括静态数据成员所占的空间),它只能在类体外进行初始化(不能在构造函数中初始化)如: int Time::minute=10;不能用初始化表对静态数据成员初始化。静态数据成员的出现可以取代全局变量的使用,但注意它的作用域只限于定义该类的作用域内,在类外可以通过对象名也可以通过类名引用公用的静态数据成员(注意:类的静态数据成员一定要初始化之后再使用)。
2、和静态数据成员一样,静态成员函数是类的一部分而不是对象的一部分。静态成员函数没有this指针,在C++程序中静态成员函数主要用来访问静态数据成员,而不访问非静态成员,注:若要访问非静态成员,必须指定特定对象。而公用成员函数可以引用本对象中的一般数据成员和静态数据成员。
问:静态成员函数可以访问私有非静态成员吗?
3、静态全局变量能被本文件使用,不能被其他文件使用,即使用extern声明也不行。(但是把头文件包含过去的话应该是可以被其他文件使用的。待验证。。。。)
4、虽然静态成员函数不属于对象。但是类的静态成员函数也能通过对象名来引用,但这并不意味着此函数是属于该对象(调用者)的。
5、如果未对静态数据成员赋初值,则编译系统会自动赋予初值0;静态数据成员可以通过对象名引用,也可通过类名引用。
6、Tips:
1>对于不只是属于某一个对象而是由各个对象共享的数据成员可以把他们定义为静态数据成员(如求成员总分,平均分类题目),同时对于那些最终结果并不属于某一个对象的题目,可以通过一静态成员函数存放结果,最后通过类名::静态成员函数输出结果。
7、搞清楚在类中把成员函数定义为静态的有什么作用?答:声明为static表示本变量或函数只被本文件使用
8、类里面定义的静态变量必须初始化后才能使用,有时不知道初始化为什么的时候,可以在初始化部分什么也不赋予。如:cv::Mat CMonitoringFactory::detectMat;
9、在类A中定义静态变量a,在类B中通过A::a的方式访问变量a,也要在A中把a设为public访问方式,否则无法访问。
有关MessageBox函数的总结:
1、MFC:
CWnd::MessageBox
int MessageBox( LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK );
////////////////////////////////华丽的分割线///////////////////
2、SDK:
int MessageBox(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
注意:MFC程序中可以利用MessageBox()函数检查程序中的错误
举例:MessageBox(NULL, L"找不到指定xml文件!", L"info", MB_OK);//windows.h
有关结构体的总结:
1、结构体不能包含函数,即C语言的结构体只能描述一个对象的状态,不能描述一个对象的行为。但注意C++中的结构体可以包含函数的。
2、要把一结构体变量的地址赋给某一个指针,要在结构体变量名前加&(对象好像也是这样的)
有关strlen、sizeof的总结:
1、注意:strlen(str1)是计算char*字符串的实际长度,size()、length()是测string字符串的实际长度,不包括字符串末尾的“ ”,而sizeof(str2)函数计算的是str2所占内存空间的长度(跟str2的字符类型有关)。
有关new()、mallo()的总结:
1、用new()与mallo()申请的内存空间是在堆上的,堆上申请的内存空间需要手动释放(注意申请了多少就要释放多少,如果没有在程序中不断地用new()申请内存空间的话,可以在析构函数中释放内存),其他方式得到的内存空间是在栈上的,栈上申请的内存空间一般不需要手动释放,有系统自动释放。
2、当单纯的创建对象的时候,对象存放在栈中,当对象的生命周期结束时,系统会自动调用析构函数,释放掉栈空间。
C++中delete和delete[]的区别
使用原则就是:new 和 delete、new[] 和 delete[] 对应使用。对于new[] 创建的对象数组,若回收时用delete而不用delete[] 的话只会回收一个对象的内存空间。
有关memcpy()的总结:
函数原型:void *memcpy(void*dest, const void *src, size_t n);
用法:#include<string.h>
功能:从源src所指的内存地址的起始位置开始,拷贝n个字节的数据到目标dest所指的内存地址的起始位置中。
说明:
1)src和dest所指内存区域不能重叠,函数返回指向dest的指针。如果src和dest以任何形式出现了重叠,它的结果是未定义的。
2)与strcpy相比,memcpy遇到’ ’不结束,而且一定会复制完n个字节。只要保证src开始有n字节的有效数据,dest开始有n字节内存空间就行。
3)如果目标数组本身已有数据,执行memcpy之后,将覆盖原有数据(最多覆盖n个)。
如果要追加数据,则每次执行memcpy()后,要将目标地址增加到要追加数据的地址。
4)source和destin都不一定是数组,任意的可读写的空间均可。当dest是指针时,一般会用到new()函数来开辟内存空间。
动态内存分配
1、用delete释放了用new申请的内存块之后记得把与之关联的变量设置为NULL。
2、块的内存是在程序运行的时候申请的(是在堆上申请的,变量,固定长度的数组是在栈上申请的),而不是在编译的时候申请的,所以叫做动态内存。
3、动态数组实现 cin>>count; int *x=new [count];cout<<x[i];
解决内存泄露问题
三种解决方法:
1、在调用的地方new,用完delete即可。
2、使函数返回类型为char*,程序末尾return tempStr;在调用的地方用完delete即可。
3、使用C++11智能指针,程序末尾返回一个智能指针,用完无需手动释放。
有关void* 型的总结:
1、任何类型指针都可以赋值给void指针,但反之则不行。
2、void型可以强制转化为其他类型。
有关typedef与define的总结
1、注意typedef与define的使用区别
例: typedef int HEIGHE define _tWinMain WinMain
2、typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
注意:上面的tagMyStruct是标识符,MyStruct是变量类型
在C中,在上面申明后申请结构体变量的方法有两种:
(1)struct tagMyStruct +变量名
(2)MyStruct +变量名
在c++中可以有
(1)struct tagMyStruct +变量名
(2)MyStruct +变量名
(3)tagMyStruct +变量名
上面结构体等价于typedef struct
{
int iNum;
long lLength;
} MyStruct;
注意struct Student
{
int a;
}stu1;//stu1是一个变量,Student是结构体类型
1、typedef struct libvlc_instance_t libvlc_instance_t;//意思就是用libvlc_instance_t来代表struct libvlc_instance_t来定义结构体。
有关指针类型的总结:
1、 指向函数的指针 :int( *p )(int ,int);//p是指向函数的指针变量。
返回指针值的函数(指针函数):类型名*函数名(参数表列)如int *a(int x,int y);
指向指针的指针,如char *(*p)或char**p ,注:指向指针的指针一般用它来指向指针数组的元素。
2、 所谓的多级指针,就是一个指向指针的指针,比如:
char *p = "my name is chenyang.";
char **pp = &p;//二级指针(要么指向一个一级指针的地址,要么指向一个二级指针变量)
char ***ppp = &pp;//三级指针
注意:多级指针可以通过多维数组的方式遍历?
2、几种实例
typedef void (__stdcall *Callback_IMPPS_RawData)(int index, IMPPS_RawData* raw_data, void* user_data);//注意此处只是声明了一个指针类型的名字Callback_IMPPS_RawData,而不是一个指针。
这个指针类型定义的指针可以用来指向有三个参数的函数。
typedef void * (*libvlc_video_lock_cb)(void *opaque, void **planes);//定义了一个指针类型的名字,该指针指向的函数的返回值的类型为void * 类型
typedef void __stdcall *Callback_IMPPS_RawData(int index, IMPPS_RawData* raw_data, void* user_data);//怎么解释???个人认为此处只是声明了一个指针函数的函数名
void (__stdcall *Callback_IMPPS_RawData)(int index, IMPPS_RawData* raw_data, void* user_data);//而此处是定义了一个指针变量。
void __stdcall *Callback_IMPPS_RawData(int index, IMPPS_RawData* raw_data, void* user_data);//而此处是定义了一个指针函数。
注意:对于形参类型是指向指针的指针的形参,传入的实参类型应该是一个指针变量的地址。
有关引用&的总结:
1、指针作为函数参数:
为了使在函数中改变了的变量值能被main函数使用,不能采用把要改变的值的变量作为参数的方法,而应该采用指针变量作为参数,在函数执行过程中,使指针变量所指向的变量值(而不是指针变量的值)发生变化,函数调用结束后,这些变量的变化依然保留下来。
2、请注意:不要试图通过改变形参指针变量的值而使实参指针变量的值改变。在C++编程中还是多训练“引用”方法。详细资料可见总结文档的《C++中 值传递、指针传递、引用传递的总结》
3、一般有引用的地方不需要指针。
4、一个函数只有一个返回值,但是可以通过引用的方法达到多个返回值的效果。
5、在数据类型名后面出现的&是引用声明符,其他场合出现的都是地址符。
有关文件file操作的总结
1、ofstream outXmlFile(xmlFileAddr, ios::out);//路径不存在的话也会创造路径
2、C++中有关文件的地址用的都是双斜杠。
3、对于文件的操作要养成良好习惯:用fopen()打开文件后同时要在“后面”写一句fclose()的语句。
向文件中写入有格式的数据时用的是fprintf(),而直接把内存中的数据写入文件的话是用fwrite();
有关作用域或生命周期的总结:
2、注意局部变量若是在函数体内最外层(即不在if、for、while语句内)定义,则其生命周期为该函数的生命周期,若在函数内的if、for、while语句内定义,则其生命周期只在if、for、while语句内。
2、在一个文件里定义的全局对象的作用域(不等于生命周期)是从该定义处开始,到本文件的末尾(注意,此处并不是到文件末尾全局对象就会被清空,而是作用域可以延伸到此处,若在别处用extern声明将该全局对象,则该全局对象的作用域可延伸自用extern定义处到对应文件末尾)。
3、extern的作用是拓宽全局变量的作用域,只用作声明,而不用作定义,只需在引用前声明即可使用,在多文件程序中,一般在.cpp文件中定义全局变量,然后在对应的.h文件中,用extern声明,在需要用到该全局变量的.cpp文件中加入那个头文件即可使用,而无需再次用extern声明。
4、函数A定义的局部变量作用域只在函数A内,即使A调用函数B。
有关循环语句的总结
1、break语句只能用在循环语句中提前结束循环过程,循环语句有三种while ,do-while,for;break语句还可用在switch语句中(在switch语句中,break用来使流程跳出switch语句,继续执行switch后的语句)。在循环语句中,break用来从最近的封闭循环体内跳出。
2、continue语句的作用是:结束当前正在执行的这一次循环(for、while、do…while),接着执行下一次循环。即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。在for循环中,continue用来转去执行表达式2。在while循环和do…while循环中,continue用来转去执行对条件表达式的判断。
3、continue语句和break语句的区别是:continue语句只结束本次循环,而不是终止整个循环的执行。而break语句则是结束本次循环,不再进行条件判断。
4、用while(1)或goto语句可以实现从键盘轮回输入数据。
关于局部(全局)数组与指针的初始化问题
局部的数组不初始化系统不会报错,但是数组中的每一点的值是未知的,而局部的指针如果不初始化就使用的话编译的过程中就会报错。对于全局的数组系统会自动将其内存空间初始化为0,而对于全局指针系统会自动将其初始化为NULL;
try-catch 用法总结
例1:try {
if (xmlReadOK == 0)
throw runtime_error("找不到指定XML文件!");
else {}
}catch (runtime_error err){
MessageBox(L"找不到指定XML文件!");
}
例2: try
{
while (cin.peek() == ' ')
{
cin.get();
}
if (cin.peek()=='5')
{
throw "输入了5";
}
cout << "zhang" << endl;
if (cin.peek() == ' ')
{
break;
}
}
catch (const char*str)
{
cout << str << endl;
}
注意:1>在某个try语句里执行过throw语句,它后面的语句(截止到这个try语句末尾)将永远不会被执行。
2>只用它们来处理可能确实不正常的情况。
3>throw()函数的妙用:throw(a!=8,”error input!”);//a等于8时将会提示错误。
extern “C”用法解析:
概念:
1>extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
比如说你用C 开发了一个DLL 库,为了能够让C ++语言也能够调用你的DLL输出(Export)的函数,你需要用extern "C"来强制编译器不要修改你的函数名。
2>被extern "C"限定的函数或变量是extern类型的;
3>extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
4>例如函数void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。
5>extern “C”这个声明的真实目的是为了实现C++与C及其它语言的混合编程。
用法:
1>在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:extern “C”{#include”cExample.h”}或extern “C”int add(int,int)。
2>而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。
回调函数注意事项:
1、回调函数必须修饰为CALLBACK并且只能声明类的为静态成员函数:
static BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam);//获取进程句柄中的主窗口句柄
这种声明方式带来了一个问题,即EnumWindowsProc只能使用类中的静态成员变量,如何解决这种矛盾呢?
2、可以使用lParam来传递指向该类的指针以访问类的非静态变量。
如:CVlcOperation*pRawData = (CVlcOperation*)lParam; pRawData ->Lock();
3、一般项目中的回调函数都需要自己去写。
4、CALLBACK修饰的函数只能说明该函数会以函数名(或该函数的指针)的方式被调用,并不代表会被系统自动反复调用。如:
IMPPS_SetRawDataCallback(CallbackIMPPS_RawData, this);
//为什么在OnBnClickedShow函数只调用一次回调函数,而显示界面可以一直更新数据,原因在于IMPPS_SetRawDataCallback这个函数的函数体里面一定写了一个一段时间就调用一次回调函数的算法,只是自己看不到。
5、自己目前接触到的回调函数在调用者中都留有让回调函数与程序中的对象交互的接口(方法是让调用者与回调函数有形参类型和名字相同的形参)。
枚举变量:
声明形式为:enum 枚举变量类型名 {变量值列表}
例如:enum WeekDay {sun,mon,tue,wed,thu,fri,sat};
定义枚举变量方式:enum WeekDay result;或WeekDay result;
亦可类型与变量同时定义(甚至类型名可省),格式如下:
enum {Sun,Mon,Tue,Wed,Thu,Fri,Sat} weekday1, weekday2;
重要提示:
1、枚举常量代表该枚举类型的变量可能取的值,编译系统为每个枚举常量指定一个整数值,缺省状态下,这个整数就是所列举元素的序号,序号从0开始。
2、可以在定义枚举类型时为部分或全部枚举常量指定整数值,在指定值之前的枚举常量仍按缺省方式取值,而指定值之后的枚举常量按依次加1的原则取值。
3、各枚举常量的值可以重复。
4、枚举常量只能以标识符形式表示,而不能是整型、字符型等文字常量。
把自己的写的功能函数编译成动态库的方法:
1>在simpledll.h文件中定义宏: #ifdef DLL_IMPLEMENT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
.h中函数声明的前面要加DLL_IMPLEMENT(.cpp中不需要?)
//该宏完成在dll项目内部使用__declspec(dllexport)导出
//在dll项目外部使用时,用__declspec(dllimport)导入
//宏DLL_IMPLEMENT在simpledll.cpp中定义
2>在对应的simpledll.cpp文件中加入宏定义 #define DLL_IMPLEMENT
注意:注意此处的宏定义需要写在#include "simpledll.h"之前
cpp文件中的导出函数前不必加DLL_IMPLEMENT
3>当某个类是从一个动态库中得到的时候,那么这个类的所有成员前都不要加__declspec(dllimport)修饰符。
注意:若自己的动态库的某个函数中需要使用某个变量,而又不想(不能)每次在函数体定义它,解决方法是把该变量作为函数的形参传进来(如写文件操作每次用fopen()函数打开文件时以前的数据就会被覆盖)。
有关Vector容器与迭代器和迭代器的总结:
1、vector不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。因此,vector<int>和vector <string>都是数据类型。
2、对于switch语句,且与容器相关的程序,注意每一项case语句后迭代器应加多少。
3、有关用到容器的文件需要加入两处代码#include<vector>与using namespace std;
4、vector容器下标操作从0开始。例如vector<string>strvector;strvector[0]
5、strvector.end()指向容器最后一个元素的下一个位置。
6、strvector.erase(it)执行后,迭代器会自动指向下一个位置,注意在循环语句中使用strvector.erase()函数时要防止出现野指针的现象。
7、strvector.size()容器有多少元素,此值就是多少。
8、注意容器和结构体的结合使用,可使容器中数据的读取特别方便。
9、注意把某个类设为容器的数据类型时可以带来的好处。
10、某个迭代器指向某个容器的某一个位置,当后面对容器进行了增加或删除工作后,迭代器的指向位置很可能会发生变化。所以要注意迭代器尽量不要定义为全局的。另外对容器的访问尽量采用下标访问方式(效率更高,不易出错)。
11、C++中我们存储数据一般采用容器来实现,而C语言则多采用结构体,编写代码过程中要灵活取用。
函数模板的使用:
1.对于函数功能完全相同,只是函数的参数类型不同的的情况,可以写一段通用代码,适用于多种不同数据类型(通用代码必须不受数据类型的影响。),简化重载函数的函数体设计、便会使代码重用率大大提高,使用模板就是这一目的。
2.函数模板的定义形式是:
template<typename 标识符>
函数定义
例如:定义一个求绝对值函数的模板
#include<iostream>
template<typename T>
T abs(T x) //形参类型为T,函数返回类型为T
{
return x<0?-x:x;
}
Void main()
{
int n=-5;
double d=-5.5;
cout<<abs(n)<<endl;
cout<<abs(d)<<endl;
}
78、注释一大段的方法:#if 0 #endif
79、注意:函数参数声音为const类型的话,那么函数内容只能读该参数值而不能去修改它。
map容器使用注意事项
1、#inclde<map> using namespace std;
2、pDeviceMap3->insert(make_pair(idPeriodsVector[userID], Result));
3、map<int, CameraRTSP*>::iterator FrameDataIt = mapFrameData.find(id); //CameraRTSP是类类型
该map容器mapFrameData的第二个元素是CameraRTSP类型的指针。
string使用总结
1、string类字符串可以直接相加,但是不能直接string型与int型相加(需进行转化)。
如:std::string tstr = "登录用时" +std::to_string((sys1.wSecond - sys.wSecond) * 1000 + (sys1.wMilliseconds - sys.wMilliseconds))+"ms";
有关字符串操作总结
1、Strcpy(str1,str2)的作用是将第二个字符数组中的字符串复制到第一个字符数组中去。
auto存储类型说明符
auto存储类型说明符声明了一个自动变量,auto对象和变量被存储在栈中,它的生命周期仅存在于它的声明所在的块(block)中,即一个只在块运行时有效的变量。
在开发实践中,有时候我们并不能非常容易地确定一个变量应该具有的数据类型。比如,将某个复杂表达式作为初始值赋值给一个新定义的变量时,我们往往很难确定这个表达式的数据类型,从而无法确定变量应有的数据类型。为了解决这个问题,C++11为我们提供了auto关键字,使用它作为某个变量定义的数据类型,编译器会根据这个变量的初始值,自动推断出这个变量合理的数据类型而无需我们人为指定。
assert使用总结
assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:#include <assert.h>
void assert( int expression );
assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
注意事项:
1、每个assert只检验一个条件
2、学会使用assert()函数来判断是否满足条件。
3、频繁的调用会极大的影响程序的性能,增加额外的开销。assert只会在debug版本做检测。
---------------------
作者:Yuanxuevaq
来源:CSDN
原文:https://blog.csdn.net/Zhangyuanxuevaq/article/details/79687052
版权声明:本文为博主原创文章,转载请附上博文链接!