C++初学知识点总结

2023-11-17

C++学习笔记

1、namespace:所谓的namespace就是指标识符的各种可见范围,C++标准程序中的所有标识符都被定义于一个名为std的namespace中。

2、iostream与iostream.h的差别,差别当然不只是一个带后缀.h,二者的代码时不一样的,带后缀的c++标准明确提出不支持了,早期的实现将标注库功能定义在全局空间里,声明在带.h的后缀的头文件里,C++标准为了和区分,也为了正确使用命名空间,规定同文件不带后缀,因此当使用带后缀.h时相当于c中调用库函数,使用的是全局命名空间,也就是早期的c++实现。当使用<iostream>时该同文件没有定义全局空间,必须使用namespace std,这样才能正确使用cout。

3、使用标注库的标识符的几种选择:(1):直接指定如std::cout<<"hell"<<std::endl;(2)使用using关键字 如using std::cout,  那(1)里就可以写成std::cout<<"hello"<<endl; (3):最方便的就是使用using namespace std;因为标准库非常的庞大,所以在选择的类的名称或函数名的时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问 题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。所以就有了<iostream.h> 和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"

4、C与C++中的命名空间特点:1)在C语言中只有一个全局作用域;2)C语言中所有的全局标识符共享同一个作用域;3)标识符之间可能发生冲突。 C++中的命名空间:1)命名空间将全局作用域分成不同的部分;2)不同命名空间中的标识符可以同名而不会发生冲突;3)命名空间可以相互嵌套;4)全局作用域也叫默认命名空间。

5、C++命名空间的使用:1)使用整个命名空间:using namespace name; 2)使用命名空间中的变量:using name::variable; 3)使用默认命名空间中的变量:::variable;4):默认情况下可以直接使用默 认命名空间中的所有标识符。

6、C++对比c实用性增强:C语⾔中的变量都必须在作用域开始的位置定义!C++中更强调语⾔的“实用性”,所有的变量都可以在需要使用时再定义。c语言中允许重复定义多个同名的变量,C++中不允许定义多个同名变量,C语言中多个同名全局变量最终会连接到全局数据区的同一个地址空间上。

7、struct类型增强:C中的struce定义了一组变量的集合,c编译器并不认为这是一种新的类型,C++中的struct是一个新的数据类型的定义声明。c++中所有变量和函数必须有类型,c语言的默认类型在C++中不合法,在C语言中int f( );表示返回值为int,接受任意参数的函数,int f(void);表示返回值为int的无参函数在C++中int f( );和int f(void)具有相同的意义,都表示返回值为int的无参函数C++更加强调类型,任意的程序元素都必须显示指明类型。

8、C++在C基本类型系统之上增加了bool,C++中的bool可取的值只有true和false,理论上bool只占用一个字节,如果多个bool变量定义在一起,可能会各占一个bit,这取决于编译器的实现。c语言返回变量的值,C++返回变量本身,C语言三目运算符返回是变量值,不能作为左值使用,C++中的三目运算符可以作为左值使用其返回变量本身,但如果三目运算符中可能返回值有一个是常量值则不可以作为左值。如(a<b? 1:b)=30; 当左值的条件:要有内存,C++编译器帮程序员取了一个地址而已。

9、增强const:const定义常量,意味着只读 const int a;与int const b;含义一样代表一个常整形数。const int *c ; c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改);int * const d; d常指针,指针不能被修改,但是它指向的内存空间可以被修改。const int * const e; 一个指向常整形的常指针,指针和所指的内存空间均不能修改。C中定义const是只读变量并不是真正的常量。 const与define的相同,C++定义的const是一个真正的常量,在const修饰的常量编译期间就已经确定下来了。C++中的const常量类似于宏定义,const int c=5; 等价于 #define c 5  但C++与宏定义不同之处在于const常量有编译器处理提供类型检查和作用域检查,宏定义由预处理器处理,单纯的文本替换。C语言中const变量是只读变量,有自己的存储空间。而C++中可能分配存储空间,也可能不分配存储空间,当const常量为全局,并且需要在其它文件中使用,会分配存储空间,当使用&操作符,取const常量的地址时,会分配存储空间,当const int &a = 10; const修饰引用时,也会分配存储空间。

#include <iostream>
using namespace std;
int main(int argc,char *argv[])
{
    int x = 3;
    int y = 4;
    int z=5;
    const int a=10;
    int const b;
    //a++;   代表⼀一个常整形数
    //b=b+1; 代表⼀一个常整形数
    const int *c=&y;  //是一个指向常整形数的指针
    cout<<*c<<endl;   //4
    //*c=10;   //所指向的内存数据不能被修改
    c=&x;      //但是本⾝身可以修改
    cout<<*c<<endl;  //3
    int *const p = &x;  // 定义指向x的指针q
    //p = &y;          // 发生错误,指针p的地址不能改变
    *p = 10;
    cout<<*p<<endl;    //10
    const int *const e=&z;//e一个指向常整形的常指针
    //*e=10; //它所指向的内存空间不能被修改
    //e=&x;  //指针不能被修改
    cout<<*e<<endl; //5
    return 0;
}

10、真正的枚举:c语言中枚举的本质就是整形,枚举变量可以是任意整形赋值,而C++中枚举变量只能被枚举出来的元素初始化。

11、变量名:变量名实质上是一段连续存储的空间的别名,是一个标号,通过变量来申请并命名存储空间,通过变量的名字可以使用存储空间。引用:变量是一段内存的引用即别名,引用可以看作一个已定义的变量的别名,引用声明:Type& name =var;1、引用没用定义,是一种关系声明,与原类型保持一致且不分配内存,与被引用的变量有相同的地址。2、声明必须初始化,一经声明不可变更,可对引用再次引用,多次引用的结果是某一变量具有多个别名。3、&符号前有数据类型时是引用,其他是取地址。引用的意义:引用作为其他变量的别名存在因此在一些场合可以替代指针,且引用作为指针来说具有更好的可读性和实用性。引用所占的大小与指针相同,引用是一个常指针。Type& name <===> Type* const name ,C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占的空间大小与指针相同。 当函数返回值为引用时,若返回栈变量不能作为其他引用的初始值(不能作为左值),当返回静态变量或全局变量就可以成为其他引用的初始值。

#include <iostream>
using namespace std;
struct T
{
	int a;
};
void printfT(T *pT)
{ 
	cout<<pT->a<<endl;
}
//pT是t1的别名,相当于修改了t1
void printfT2(T &pT)
{ 
	pT.a=1;
	cout<<pT.a<<endl;
}
//pT和t1的是两个不同的变量
void printfT3(T pT)
{ 
	cout<<pT.a<<endl;
	pT.a=2;
}
int get1(){
	int x;
	x=10;
	return x;
}
/*int& get2(){
	int x;
	x=10;
	return x;
}*/
int& get3(){
	static int x;
	x=10;
	return x;
}
int main()
{
	int a=10;
	int b=11;
	int &r=a;
	cout<<sizeof(r)<<endl; //4 引用所占大小与指针相同,引用是一个常指针
	//Type &name <==>Type* const name
	//int &r=b; //不可以改变原因用关系
	//float &rr=a; //引用类型不匹配
	int &rr=r; //可对引用再引用 a有r和rr两个别名
	a=11;  //直接修改
	cout<<a<<endl; //11
	//间接修改 1、定义两个变量 (一个实参一个形参)2、建立关联 实参取地址传给形参
//3、*p形参去间接的修改实参的值
	{
	   int *p = &a;
	   *p = 12;
	   cout<<a<<endl; //12 引用在实现上,只不过是把
		//间接赋值成立的三个条件的后两步和二为一
	}
	b=14; //与被引用的变量有相同的地址
	cout<<"a= "<<a<<" b="<<b<<endl;
	//当对引用进行操作赋值时,编译器帮我们隐藏*操作
    // cout<<a其实是cout<<*a;*被编译器隐去
	T t1;
	t1.a=0;
	printfT(&t1); //0
	printfT2(t1); //1
	printfT3(t1); //1
	int a1=0;
	int a2=0;
	a1=get1();
	//a2=get2(); //函数的返回值为函数内部定义变量的引用,但函数在调用完毕后
	//函数内部定义的变量空间被释放,无法访问,从而造成的错误
	//int &a3=get2(); //将一个引⽤用赋给另一个引⽤用作为初始值//
	//由于是栈的引用,不能成为其它引用的初始值(不能作为左值使用
	cout<<a1<<endl;
	a2=get3(); //10 将一个引用赋给一个变量会有拷⻉贝动作
	//理解:编译器类似做了如下隐藏操作,a2=*(getA2())
	cout<<a2<<endl;
	int &a3=get3(); //由于是静态区域,内存合法,即返回静态变量或全局变量可做其他
	//引用的初始值(可做右值也可做左值使用)
	cout<<a3<<endl; //10
	return 0;
}

12、const 引用:const引用可以防止对象的值被随意修改,1、const对象的引用必须是const的,将普通引用绑定到const对象是不合法的,const int &y =x;//常引⽤用是限制变量为只读不能通过y去修改x了;2)const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是 const 引用与普通引用最大的区别。定义const int &a=2; 与  double x=3.14; const int &b=a; 都是合法的。结论:1)const int & e 相当于 const int * const e;2)普通引用 相当于 int *const e;3)当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名;4)使用字面量对const引用初始化后,将生成一个只读变量。

#include <iostream>
using namespace std;
struct Teacher
{   char name[64];
	int age;
};
//在被调用函数获取资源
int getTeacher(Teacher **p)
{
	Teacher *tmp = NULL;
	cout<<"p - "<<p<<endl;
	if(p == NULL)
	{
		return -1;
	}
	tmp = (Teacher *)malloc(sizeof(Teacher));
	if (tmp == NULL)
	{
		return -2;
	}
	tmp->age = 3;
	//p是实参的地址 *实参的地址去间接的修改实参的值
	*p = tmp;
	return 0;
}
//指针的引用做函数参数
int getTeacher2(Teacher* &myp)
{
	//给myp赋值相当于给main函数中的pT1赋值
	myp =(Teacher *)malloc(sizeof(Teacher));
	if (myp==NULL)
	{
		return -1;
	}
	myp->age=6;
	return 0;
}
void FreeTeacher(Teacher *pT1)
{
	if(pT1==NULL){
		return ;
	}
	free(pT1);
}
int main()
{
	Teacher *pT1=NULL;
	//c语言中的二级指针
	cout<<"Teacher *pT1=NULL   " <<pT1<<endl;
	getTeacher(&pT1);
	cout<<"Teacher &pT1   " <<&pT1<<endl;
	cout<<"age:"<<pT1->age<<endl; //3
	FreeTeacher(pT1);
	//c++中的引用(指针的引用)
	//引用的本质间接赋值后2个条件让c++编译器帮我们程序员做了。
	getTeacher2(pT1);
	cout<<"age:"<<pT1->age<<endl; //6
	FreeTeacher(pT1);
	int a=10;
	int &x=a;
	cout<<"x="<<x<<endl;
	int b=10;
	const int &y=b;
	//y=11; //常引用限制变量为只读不可以通过y进行修改
	const int c=1;
	//int &z=c; //常引用不可以绑定到const对象
	double d=3.14;
	const int &w=d;
	cout<<"w= "<<w<<endl; //3 const 引用可使用相关类型的对象
	//(常量非同类型的变量或表达式)初始化
	double val = 3.14;
	const int &ref =val;
	double & ref2 = val;
	cout<<ref<<" -- "<<ref2<<endl; //3 -- 3.14
	val = 4.14;
	cout<<ref<<" -- "<<ref2<<endl; //3 --4.14
	return 0;
}

13、inline内联函数:c 语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用 的开销。但是由于宏函数的处理发生在预处理阶段,缺失了语法检测和有可能带来的语 意差错。C++提供的inline实现真正的内嵌。特点:1)内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。2)C++编译器直接将函数体插入在函数调用的地方 。3)内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)。4)内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型等)。5) 内联函数由编译器处理,直接将编译后的函数体插入调用的地方,宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程。6)C++中内联编译的限制:不能存在任何形式的循环语句;不能存在过多的条件判断语句;函数体不能过于庞大;不能对函数进行取址操作;函数内联声明必须在调用语句之前;7)编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开
销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。

14、默认参数和占位参数:通常情况下,函数在调用时形参从实参那里取得值。对于多次调用一函数同一 实参时,C++给出了更简单的处理办法。给形参以默认值,这样就不用从实参那里取值了。若你填写参数,使⽤用你填写的,不填写默认void myPrint(int x= 3)在默认参数规则,如果默认参数出现,那么右边的都必须有默认参数。占位参数只有参数类型声明,⽽而没有参数名声明一般情况下,在函数体内部⽆无法使⽤用占位参数。

#include <iostream>
using namespace std;
void myprint(int x=3){
cout<<"x= "<<x<<endl;
}
void myprint1(float a, float b=4, float c=5){
//如果默认参数出现,那么右边的都必须有默认参数
cout<<a*b*c<<endl;
}
void myprint2(int a, int b, int){ 
//占位参数只有参数类型声明,而没有参数名声明
cout<< a+b<<endl;
}
int main()
{
	int a=6;
	myprint();  //x= 3
	myprint(a); //x= 6
	myprint1(1); // 20
	myprint1(1,2); //10
	myprint1(1,2,3); //6
	//myprint2(1,2); //必须把最后⼀一个占位参数补上
	//若myprint2(int a, int b, int=0)结合默认,皆可调用
	myprint2(1,2,3); //3
   	return 0;
}

15、函数重载:用同一个函数名定义不同的函数,当函数名和不同的参数搭配是函数的含义不同。重载的调用准则:1)严格匹配,找到则调用。2)通过隐式转换寻求一个匹配,找到则调用。编译器调用重载函数的准则: 1)将所有同名函数作为候选者;
2)尝试寻找可行的候选函数;3)精确匹配实参;4)通过默认参数能够匹配实参;5)通过默认类型转换匹配实参;6)匹配失败;7)最终寻找到的可行候选函数不唯一,则出现二义性,编译失败。8)无法匹配所有候选者,函数未定义,编译失败。

16、重载的底层实现:C++利用 name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。实现原理:用 v c i f l d 表示 void char int float long double 及其引用。void func(char a, int b, double c); //func_cid(char a, int b, double c),一个函数不能即作为重载又做为默认参数的函数,当你少写一个参数时系统无法确认是重载还是默认参数。总结:1)重载函数在本质上是相互独立的不同函数;2)函数的函数类型是不同的;3)函数返回值不能作为函数重载的依据;4)函数重载是由函数名和参数列表决定的。

#include <iostream>
using namespace std;

//C++利用 name mangling(倾轧)技术,来改名函数名,区分参数不同的同
//名函数。实现原理:用 v c i f l d 表示 void char int float long double
//及其引用。
void print(int a) //print_i(int a)
{
	cout<<a<<endl;
}
/*void print(int a,int b=0){ //不能既作重载,又作默认参数的函数调用
//会存在二义性编译不回通过。
	cout<<a+b;
}*/
void print(double a) //print_d(double a)
{
	cout<<a<<endl;
}
int main()
{
	//调用准则,1、所有同名函数列为候选,2、尝试寻找可行候选,3、精确匹配实参
	//4、通过默认参数匹配,5、通过类型转换匹配,6、匹配失败,7最终找到可行不唯一
	//8、无法匹配所有候选者,函数未定义,编译失败
	print(1);  //print(int)
	print(1.1); //print(double)
        print('q'); //print(int)
	print(1.11f); //print(double)
   	return 0;
}

17:类的封装与访问控制:c语言封装的概念:当单一变量无法完成描述需求的时候,结构体可以将多个类型打包成一体,行成新的类型。struct中所有行为和属性都是public的(默认),C++中可以指定行为和属性的访问方式。封装可以做到对内开放数据,对外屏蔽数据和提供接口,达到信息隐蔽的功能。struct和class关键字区别:在用struct定义类时,所有成员的默认属性为public
在用class定义类时,所有成员的默认属性为private。

#include <iostream>
using namespace std;
class cir{
	public:
	double r;
	double pi=3;
	double area=r*pi;
	public:
	void print(){
		cout<<r<<endl;     //2
		cout<<pi<<endl;    //3
		cout<<area<<endl;  //6.22447e-317
	}
};
int main()
{
	cir pi;
	cout<<"pi.r "<<pi.r<<endl;        //pi.r 2.07485e-317
	cout<<"pi address "<<&pi<<endl;   //pi address 0x7ffd5abf8320
	cout<<"pi.pi  "<<pi.pi<<endl;     // pi.pi  3
	cout<<"pi.pi address "<<&pi.pi<<endl; //pi.pi address 0x7ffd5abf8328
	pi.r=2;
	cout<<"pi.r "<<pi.r<<endl;            //2
	cout<<"pi.r address "<<&pi.r<<endl;   //pi.r address 0x7ffd5abf8320 
	cout<<"pi.area address "<<&pi.area<<endl;  //pi.area address 0x7ffd5abf8330
	cout<<pi.area<<endl;  //6.22454e-317
	pi.print();
   return 0;
}

18、对象的构造与析构:构造函数--C++提供的一个给对象初始化的方案。C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数。规则: 1)在对象创建时自动调用,完成初始化相关工作。2)无返回值,与类名同,默认无参,可以重载,可默认参数。3) 一经实现,默认不复存在。析构:C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数。规则: 1)对象销毁时,自动调用。完成销毁的善后工作。2)无返值 ,与类名同。无参。不可以重载与默认参数。析构函数的作用,并不是删除对象,而在对象销毁前完成的一些清理工作。

#include <iostream>
using namespace std;
class Test{
	public:
	Test(){
		cout<<"Test() a= "<<a<<endl;
	}
    Test(double temp){
	   a=temp;
		cout<<"Test(int a) a= "<<a<<endl;
	}
	 Test(const Test &temp){
	   a=temp.a;
		cout<<"Test(const Test &temp) a= "<<a<<endl;
	}
	~Test(){
		cout<<"~Test() a= "<<a<<endl;
	}
	void printT(){
	cout<<"printT() a= "<<a<<endl;
	}
	public:
	double a;

};
Test t(){
  Test temp(1);
	return temp;
}
void test1()
{
	t();
}
void test2(){
   Test m=t();
	//如果⽤用匿名对象初始化另外一个同类型的对象,匿名对象转成有名对象,不会析构掉
	cout<<"test2 a"<<endl;
}
void test3(){
   Test m1(1);
	m1=t();//如果⽤用匿名对象赋值给另外一个同类型的对象,匿名对象被析构
	cout<<"test3 a"<<endl;
}
void fun(Test t){
cout <<"func begin"<<endl;
cout<<t.a<<endl;
cout<<"func end"<<endl;
}
int main()
{
	//Test t;
	//Test t1(2);
	//Test t2=t1;
	//Test t3(t1);
	//t.printT();
	//t1.printT();
	//t2.printT();
	//fun(t1);
	//test1();
	test2();
	//test3();
   return 0;
}

19、构造函数的分类及调用:有参、无参与拷贝构造函数。拷贝构造:由己存在的对象,创建新对象。也就是说新对象,不由构造器来构造,而是由拷贝构造器来完成。拷贝构造器的格式是固定的。二个特殊的构造函数:1)默认无参构造函数,当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空;2)默认拷贝构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制。

20、构造函数规则: 当类中定义了拷⻉贝构造函数时,c++编译器不会提供⽆无参数构造函数;/当类中定义了有参数构造函数是,c++编译器不会提供⽆无参数构造函数;在定义类时,只要你写了构造函数,则必须要⽤。系统提供默认的拷贝构造器,一经定义不再提供。但系统提供的默认拷贝,构造器是等位拷贝,也就是通常意义上的浅拷贝。如果类中包含的数据元素全部在栈上,浅拷贝也可以满足需求的。但如果堆上的数据,则会发生多次析构行为。

#include<iostream>
#include<assert.h>
using namespace std;
class Rect
{
public:
    Rect()
    {
     p=new int(100);
    }
    
    Rect(const Rect& r)
    {
     w=r.w;
     p=new int(100);
     *p=*(r.p);
    }
     
    ~Rect()
    {
     assert(p!=NULL);
        delete p;
    }
public:
    int w;
    int *p;
};
int main()
{
    Rect rect1;
    cout<<rect1.p<<endl;  /如果没有实现深拷贝,p指向地址一样的
    Rect rect2(rect1);
    cout<<rect2.p<<endl;
    return 0;
}

21、构造函数初始化列表:如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数。如当A的对象是B类的一个成员的时候,在初始化B对象的时候,⽆无法给B分配空间,因为⽆无法初始化A类对象。当类成员中含有一个const对象或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。初始化列表中的初始化顺序,与声明顺序有关,与前后赋值顺序无关。

#include <iostream>
using namespace std;
class ABCD{
 public:
	ABCD(int a,int b,int c){
	    _a=a;
	    _b=b;
	    _c=c;
	    cout<<"ABCD() a "<<_a <<" b "<<_b<<" c "<<_c<<endl;
	 }
	ABCD(int a, int b){
	    _a=a;
	    _b=b;
	    ABCD(a,b,100);	
	}
	~ABCD()
	{
	    cout<<"~ABCD() a "<<_a <<" b "<<_b<<" c "<<_c<<endl;
	}
	int getA(){
	    return _c;
	}
	void setA(int val){
	    _c=val;
	}
     private:
	int _a;
	int _b;
	int _c;
};
int main(void)
{
   ABCD t1(1,2);
	cout<<" main "<<t1.getA()<<endl;  //0
   return 0;
}

22、对象动态建⽴立和释放new 和delete,在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。new和delete是运算符,不是函数,因此执行效率高。用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。malloc不会调用类的构造函数,而new会调用类的构造函数;Free不会调用类的析构函数,而delete会调用类的析构函数。

23、静态成员变量和成员函数:在 C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供 所有对象共用。所以在所有对象中都可以共享它。使用静态成员变量实现多个对象之间 的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。类的静态成员,属于类,也属于对象,但终归属于类。1)static 成员变量实现了同类对象间信息共享。2) static 成员类外存储,求类大小,并不包含在内。3) static成员是命名空间属于类的全局变量,存储在data区。4) static成员只能类外初始化。5) 可以通过类名访问(无对象生成时亦可),也可以通过对象访问。

#include <iostream>
using namespace std;
class Box{
 public:
	 Box(int l,int w):length(l),with(w){
	     
	 }
	int volume(){
	    return length*with*height;
	}
	static int height;
	int length;
	int with;
};
int Box::height=5; //成员是命名空间属于类的全局变量,存储在data区。只能类外初始化
int main(void)
{
   cout<<sizeof(Box)<<endl; //8 static 成员类外存储,求类大小并不包含在内
	Box b(1,2);
	cout<<sizeof(b)<<endl; //8 
	cout<<Box::height<<endl; //通过类名访问
	cout<<b.height<<endl;    //通过对象访问
   return 0;
}

24、静态成员函数:静态函数不在于信息共享,数据沟通而在于管理静态数据成员, 完 成对静态数据成员的封装,且静态成员函数只能访问静态数据成员。原因:非静态成员函数,在调用时this指针被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this指针。

25、编译器对属性和方法的处理机制:C++类对象中的成员变量和成员函数是分开存储的; 普通成员变量存储于对象中,与struct变量有相同的内存布局和字节对齐方式; 静态成员变量:存储于全局数据区中成员函数:存储于代码段中。很多对象共用一块代码,代码如何区分具体对象,如:int getK() const { return k; },代码是如何区分,具体obj1、obj2、obj3对象的k值?C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。(静态成员函数不具有普通函数指向具体对象的指针)如果成员函数的形参和类的属性名字相同通过this指针来解决。

26、全局函数与成员函数:1、把全局函数转化成成员函数,通过this指针隐藏左操作数Test add(Test &t1, Test &t2)===》Test add(Test &t2);2、把成员函数转换成全局函数,多了一个参数 void printAB()===void printAB(Test *pthis);

27、友元:采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函 数一般定义为公有的,依此提供类与外界间的通信接口。但是有时需要定义一些函数,这 些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还有友元类两者统称为友元。友元的作用是提高了程 序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装 性和隐藏性,使得非成员函数可以访问类的私有成员。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函 数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字 friend,其格式如下:friend 类型 函数名(形式参数); 一个函数可以是多个类的友元函数,只需要在各个类中分别声明。友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。

28、论友元:友元声明以关键字 friend开始,它只能出现在类定义中。因为友元不是授权类的 成员,所以它不受其所在类的声明区域 public private 和 protected 的影响。通常我们选择把所有友元声明组织在一起并放在类头之后。友元不是类成员,但是它可以访问类中的私有成员。友元的作用在于提高程序的运 行效率,但是它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。 不过类的访问权限确实在某些应用场合显得有些呆板,从而容忍了友元这一特别语法现象。(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类;B的友元,要看在类中是否有相应的声明。(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定 是类A的友元,同样要看类中是否有相应的声明。

29、运算符重载:所谓重载就是重新赋予新的意义,运算符重载是对一个已有的函数赋予新的含义,使之有新功能,因此一个函数名就可以用来代表不同的功能的函数即一名多用。运算符重载的本质是函数重载,格式为: 函数类型 operator 运算符名称(形参表列) { 重载实体; }  operator 运算符名称在一起构成了新的函数名。

30、重载规则:1)大部分运算符都可以重载不允许用户自己定义新的运算符只能在已有的C++运算符进行重载,C++不能重载的运算符有. ; .*  ;  ::  sizeof前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符合; sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征。2)重载不能改变运算符的运算对象即操作数的个数;3)重载不能改变运算符的优先级;4)重载不能改变运算符的结合性;5)重载运算符的函数不能有默认参数否则就改变了运算符参数的个数,与前面第(2)点矛盾。6)重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一 个是类对象(或类对象的引用)。7)用于类对象的运算符一般必须重载,但有两个例外,运算符”=“和运算符”&“不 必用户重载。 复制运算符”=“可以用于每一个类对象,可以用它在同类对象之间相互赋值。因为系统已为每一个新声明的类重载了一个赋值运算符,它的作用是逐个复制类中的 数据成员地址运算符&也不必重载,它能返回类对象在内存中的起始地址。 (8)应当使重载运算符的功能类似于该运算符作用于标准类型数据时候时所实现的功能。例如,我们会去重载”+“以实现对象的相加,而不会去重载”+“以实现对象相减的功能,因为这样不符合我们对”+“原来的认知。 (9)运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数。

31、继承和派生:类与类之间的关系:1)has-A:包含关系,用以米哦啊书一个类由多个“零部件”构成,实现has-A关系用类成员表示,即一个类中的数据成员是另一个类已定义的类。2)uses-A:一个类部分地使用另一个类,通过类之间成员函数的相互联系定义友元或者对象参数传递实现。3)is-A:机制为继承,关系具有传递性不具有对称性。

32、类的继承是新的类从已有的类那里得到已有的特性,或从已有的类产生新类的过程就是类的派生,原有的类叫做基类或者父类,产生的新类叫派生类或者子类。派生和继承是同一种意义的两种称谓,isA的关系。派生类中的成员包含两部分,一类是从父类中继承过来的体现其共性,一类是自己增加的成员,体现其个性。继承特点:全盘接收,除了构造器和析构器,基类可能会造成派生类成员冗余,因此基类是需要设计的,继承方式 class 派生类: [继承方式] 基类名 { 派生类成员声明; }  。 一个派生类可以有多个基类称之为多重继承,派生类只有一个称之为单继承。

33、派生类成员的标识和访问:公有继承:公有继承中基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可访问,派生类的其他成员可以直接访问他们,无论派生类的成员还是派生类的对象无法访问基类私有成员;私有继承:当以私有继承的方式实现时,基类中的公有和保护成员都以私有成员身份出现在派生类中,派生类的其他成员可以直接访问他们,但在类外部通过派生类的对象无法访问,而基类中的私有成员在派生类中无论是派生类成员还是通过派生类的对象都不可访问从基类派生而来的私有成员;保护继承:保护继承基类的公有和私有成员都以保护成员的身份出现在派生类中,派生类的其他成员可以直接访问,但是通过类外部对象无法访问,无论派生类的成员还是派生类的对象都不可以访基类的私有成员。(private成员在子类中依然存在只是无法访问)。

#include <iostream>
using namespace std;
class Base{
public:
	int pub;
protected:
	int pro;
private:
	int pri;
public:
	Base(){
		pub=0;
		pro=0;
		pri=0;

	}
	void set(int a,int b,int c){
		pub=a;
		pro=b;
		pri=c;
		cout<<"struct a "<<pub<<" b "<<pro<<" c "<<pri<<endl;
	}
};
class son:public Base{
	public:
	void fun(){
		pub=10;
		pro=11;
		//pri=12; //error
	}
	
};
class son1:protected Base{
	public:
	void fun(){
		pub=10;
		pro=11;
		//pri=12;
	}
};
class son2:private Base{
	public:
	void fun(){
		pub=10;
		pro=11;
		//pri=12;
	}
};
int main()
{
	Base b;
	b.pub=1;
	//b.pro=2;  //error
	//b.pri=3;  //error
	b.set(1,2,3);
	son s;
	s.pub=0;
	//s.pro=2;
	//s.pri=3;
	s.fun();
	s.set(1,2,9);
	son1 s1;
	//s1.pub=1;
	//s1.pro=2;
	//s1.pri=3;
	//s1.set(1,2,3);
	son2 s2;
	//s2.pub=1;
	//s2.pro=2;
	//s2.pri=3;
	//s2.set(1,2,3);
   return 0;
}

34、继承中的构造与析构:类型兼容型原则:是指在需要基类对象的任何地方都可以用共有派生的对象来替代,通过共有继承,派生类得到基类中除构造和析构之外的所有成员,这样公有派生实际就具备了和基类所有相同的功能,这样:1)子类对象就可以当作父类对象使用,2)子类对象可以直接赋值给父类对象。3)子类对象可以直接初始化父类对象;4)父类指针可以直接指向子类对象;5)父类引用可以直接引用子类对象。在替代完成后,派生类可以作为基类使用但是只能使用从基类继承的成员。(子类就是特殊的父类)

34、继承中的对象模型:类在C++编译器内部可以理解为结构体,子类是是由父类成员叠加子类新成员得到的。继承中的构造与析构调用规则:1)子类对象初始化时会先调用父类的构造函数;2)父类构造函数执行结束执行子类构造函数;3)当父类构造函数有参数时需要在子类初始化列表中显示调用;4)析构函数的调用顺序与构造函数相反。先构造自己再构造成员变量最后自己;析构是先自己再成员变量最后析构父类。

#include <iostream>
using namespace std;
class Base{
public:
	Base(){};
	Base(const char* s){
		this->s=s;
		cout<<"Base() "<<" s "<<s<<endl;
	}
	~Base(){
		cout<<"~base() " <<endl;
	}
	void print(){
		cout<<"base   "<<endl;
	}
	private:
	const char *s;
};
class son:public Base{
	public:
	son(){}
	son(int a):Base("base from son"){
		cout<<"child() "<<endl;
		this->a=a;
	}
	son(int a, const char *s):Base(s){
		cout<<"son"<<endl;
		this->a=a;
	}
	~son(){
		cout<<"~son"<<endl;
	}
	public:
	void fun(){
		cout<<"son   "<<endl;
	}	
	private:
	int a;
};
void print0(Base *b)
{
	b->print();
}
void print1(Base &b){
	b.print();
}
int main()
{
	son s;
	s.fun(); //son
	s.print(); //base
	Base *b=NULL;
	b=&s;
	b->print(); //base
	//b->fun();
	son s1;
	Base b1;
	print0(&b1); //base
	print0(&s1); //base
	print1(b1); //base
	print1(s1); //base
	son s2;
	Base b2=s2;
	
	son s3(10);//子类对象在创建时会首先调用父类的构造函数
	son so(10,"ps");//父类构造函数执行结束后,执行子类的构造函数当父类的构造函数
	//有参数时,需要在子类的初始化列表中显示调用,析构函数调用的先后顺序与构造函数相反
    return 0;
}

35、继承中同名成员处理方法:当子类成员变量与父类成员变量同名时子类依然从父类中继承同名成员;在子类中通过域分辨符进行同名成员区分;同名成员存储位置在不同位置;派生类中static关键字:基类中的静态成员将被所有派生类共享,派生类访问成员方式是 类名::成员。或对象名.成员。

#include <iostream>
using namespace std;
class Base{
public:
	int a,b;
	static int i;
	static void Add(){i++;}
	void fun(){
		cout<<"Base func"<<endl;
		cout<<"base statc "<<i<<endl;
	}
};
int Base::i=0;
class son:public Base{
	public:
	int b,c;
	void fun(){
		cout<<"son fun"<<endl;
		i=3;
		cout<<"son static i "<<i<<endl;
		Base::i++;
		Base::Add();
	}
};

int main()
{
	son s;
	s.a=1;
	s.b=2;
	s.Base::b=3;
	s.c=4;
	cout<<"s.b= "<<s.b<<endl;//同名成员变量
	s.fun();
	s.Base::fun(); //同名成员函数通过作用域分辨符进行区分
	cout<<"base:: i "<<Base::i<<" son i "<<s.i<<endl;
    return 0;
}

36、多继承:一个类有多个直接基类的继承关系成为多继承,派生类::派生类名(总参数表):基类名 1(参数表1),基类名(参数表 2)...基类名 n(参数表n),内嵌子对象1(参数1),内嵌子对象2(参数2)...内嵌子对象n(参数表n){派生类新增成员初始化语句;};如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时可能产生二义性;继承的公共路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类产生多个基类子对象,要使基类在派生类只产生一个对象必须对这个基类声明为虚继承,是基类成为虚基类(virtual)。

#include <iostream>
using namespace std;
class B{
public:
	int b;
	B(){
		cout<<"Base () "<<endl;
	}
	
	void fun(){
		cout<<"base i "<<b<<endl;
	}
};
class B1:public B{
	public:
	B1(){
		cout<<"B1 ()"<<endl;
	}
	private: int b1;
};
class B2:public B{
	public:
	B2(){
		cout<<"B2 ()"<<endl;
	}
	private: int b2;
};
class C:public B1, public B2
{
	public:
	int f();
	private:
	int d;
};
int main()
{
	C s;
	//s.b=10;
	//s.B::b=1;
	s.B1::b=0;
	s.B2::b=9;
    return 0;
}

37、多态:如果有几个相似却不完全相同的对象,向他们发出同一消息时他们有不同的反应执行不同的操作,这就叫多态。C++中所谓的多态是指由继承产生的相关不同的类,其对象对同一消息做出的不同响应。多态实现的前提:赋值兼容规则->指的是需要基类对象的任何地方都可以使用共有派生类的对象来代替。在替代后派生类可以作为基类的对象使用,但是只能使用从基类继承的成员。多态实现的条件:1)要有继承;2)要有虚函数重写;3)要有父类指针(父类引用)指向子类对象。

#include <iostream>
using namespace std;
class B{
public:
	B(int b){
		this->b=b;
		cout<<"Base () "<<b<<endl;
	}
	void fun(){
		cout<<"fun base i "<<b<<endl;
	}
	virtual void fun1(){
		cout<<"virtual Base fun1 "<<endl;
	}
private:
	int b;
};
class B1:public B{
public:
	B1(int b):B(10){
		this->b=b;
		cout<<"B1 () "<<b<<endl;
	}
	void fun(){
		cout<<"fun b1 "<<b<<endl;
	}
	virtual void fun1(){
		cout<<"virtual B1 fun1 "<<endl;
	}
private:
	int b;
};
void print0(B *base){
	base->fun();
}
void print1(B &base){
	base.fun();
}
int main()
{
	B *base=NULL;
	B p1(20);//Base () 20
	B1 b1(30); //Base () 10 B1 () 30
	base=&p1;
	base->fun(); //fun base i 20
	base->fun1(); //virtual Base fun1
	base =&b1;
	base->fun(); //fun base i 10
	base->fun1(); //virtual B1 fun1 多态成立要有继承\要有虚函数重写
	//要有父类指针(父类引用)指向子类对象
	b1.fun(); //fun b1 30
	b1.fun1();//virtual B1 fun1 
	B &b2=p1;
	b2.fun();//fun base i 20
	B &b3=b1; 
	b3.fun();//fun base i 10
	print0(&p1);  //fun base i 20
	print0(&b1); //fun base i 10
	print1(p1); //fun base i 20
	print1(b1); //fun base i 10
    return 0; 
}

38、静态联编和动态联编:联编是指一个程序模块、代码之间互相关联的过程。2、静态联编(static  binding),是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。3、动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编(迟绑定)。switch语句和 if 语句是动态联编的例子。C++与C相同是静态编译型语言,在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象;所以编译器认为父类指针指向的是父类对象。由于程序没有运行,所以不可能知道父类指针指向的具体是父类对象还是子类对象,从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译的结果为调用父类的成员函数。这种特性就是静态联编。多态的发生是动态联编,实在程序执行的时候判断具体父类指针应该调用的方法。

39、虚析构函数:构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数。析构函数可以是虚的。虚析构函数用于指引delete运算符正确析构动态对象  。多态的实现原理:当基类中声明虚函数,编译器会在类中生成一个虚函数表。虚函数表是一个存储类成员函数指针的数据结构,表由编译器自动生成和维护,虚函数成员会被编译器放到虚函数表中,存在虚函数时每个对象都有一个指向虚函数表的指针(vptr指针)。

#include <iostream>
#include<string.h>
using namespace std;
class A{
public:
	A(){
		p=new char[20];
		strcpy(p,"obja");
		cout<<"Base A "<<endl;
	}
	virtual ~A(){
		delete [] p;
		cout<<" ~A "<<endl;
	}
private:
	char *p;
};
class B:public A{
public:
	public:
	B(){
		p=new char[20];
		strcpy(p,"objb");
		cout<<"son B "<<endl;
	}
	virtual ~B(){
		delete [] p;
		cout<<" ~B "<<endl;
	}
private:
	char *p;
};
class C:public B{
	public:
	C(){
		p=new char[20];
		strcpy(p,"objc");
		cout<<"son C "<<endl;
	}
	virtual ~C(){
		delete [] p;
		cout<<" ~C "<<endl;
	}
private:
	char *p;
};
//通过父类指针把所有的子类对象的析构函数都执行一遍释放所有的子类资源
void deletep(A *base){
	delete base;
}
int main()
{
	C *myC=new C; //Base A son B son C
	//delete myC; //~C  ~B  ~A
	deletep(myC); //无virtual -A 。有~C  ~B  ~A
    return 0; 
}

40、重载(添加):1)相同的范围(在同一个类中);2)函数名字相同;3)参数不同;4)virtual关键字可有可无。重写(覆盖) 是指派生类函数覆盖基类函数,特征是:1)不同的范围,分别位于基类和派生类中;2)函数的名字相同;3)参数相同;4)基类函数必须有virtual关键字。重定义(隐藏) 是指派生类的函数屏蔽了与其同名的基类函数,规则如下:1)如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无virtual,基类的函数被隐藏。2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有vitual关键字,此时基类的函数被隐藏。

41、纯虚函数与抽象类:纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本纯虚函数为个派生类提供一个公共界面(接口的封装和设计、软件的模块功能划分)。virtual  类型  函数名(参数表)  =  0; 一个具有纯虚函数的基类称为抽象类。1)含有纯虚函数的类,称为抽象基类,不可实列化。即不能创建对象,存在 的意义就是被继承,提供族类的公共接口。2)纯虚函数只有声明,没有实现,被“初始化”为 0。3)如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,则该虚函数在派生类中仍然为纯虚函数,派生类仍然为纯虚基类。

42、抽象类在多继承中的应用:绝大多数面向对象语言都不支持多继承,绝大多数面向对象语言都支持接口的概念,C++中没有接口的概念,C++中可以使用纯虚函数实现接口,接口类中只有函数原型定义,没有任何数据的定义。

43、模版:泛型(Generic Programming)即是指具有在多种数据类型上皆可操作的含意。泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件。泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库)。其语言支持机制就是模板(Templates)。模板的精神其实很简单:参数化类型。换句话说,把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来
做成模板参数T。所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。

#include <iostream>
using namespace std;
int Max(int a,int b){
	cout<<" int max(int a,int b)"<<endl;
	return a>b?a:b;
}
template<typename T>
T Max(T a, T b){
	cout<<" T amx(T a, T b)"<<endl;
	return a>b?a:b;
}
template <typename T>
T Max(T a, T b,T c)
{
	cout<<"T Max(T a,T b,T c)"<<endl;
	return max(max(a,b),c);
}
int main()
{
   int a=1,b=2;
	cout<<Max(a,b)<<endl;//当函数模板和普通函数都符合调用时,优先选择普通函数
	cout<<Max<>(a,b)<<endl; //显示使用函数模板
	cout<<Max(3.0,4.0)<<endl; //模板函数模板产生更好的匹配使用函数模板
	cout<<Max(5.0,6.0,7.0)<<endl; //重载
	cout<<Max('a',100)<<endl;//调用普通函数可以隐式类型转换
   return 0;
}

44、函数模版的调用是先将模版实化为函数然后调用,函数模版只适用于函数参数相同而类型不同,且函数体相同的情况。如果个数不同则不能使用函数模版。普通函数会提供隐士的数据类型转换,模版不提供,必须 严格匹配。当普通函数和模版都匹配是优先选择普通函数。若显示使用函数模版则使用<>类型列表。编译器的编译过程:编译器并不是把函数模版处理成能够处理任意类的函数,而是编译器从函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

45、类模板:类模板与函数模板的定义和使用类似,有两个或多个类,其功能是相同的仅仅是数据类型不同,所以将类中的类型进行泛化。 模板类派生普通类:子类从模板类继承的时候,需要让编译器知道父类的数据类型具体是什么(数据类型的本质:固定⼤大小内存块的别名)A<int> 。

46、类型转换:类型转换有c风格的,当然还有 c++风格的。c风格的转换的格式(TYPE) EXPRESSION,但是 c 风格的类型转换有不少的缺点,有的时候用 c 风格的转换是不合适的,因为它可以在任意类型之间转换,比如你可以把一个指向const 对象的指针转换 成指向非 const 对象的指针,把一个指向基类对象的指针转换成指向一个派生类对象的指针,这两种转换之间的差别是巨大的,但是传统的 c 语言风格的类型转换没有区分这些。还有一个缺点就是,c 风格的转换不容易查找,他由一个括号加上一个标识符组成, 而这样的东西在 c++程序里一大堆。所以 c++为了克服这些缺点,引进了 4个新的类型转换操作符。static_cast 静态类型转换; reinterpreter_cast 重新解释类型转换。dynamic_cast 子类和父类之间的多态类型转换。const_cast 去掉const属性转换。const_cast<目标类型>(标识符),目标类类型只能是指针或引用。

47、异常:异常是一种程序控制机制,与函数机制独立和互补。函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈结构,却可以同时设置多个异常类型作为网捕条件,从而以类型匹配在栈机制中跳跃回馈。异常设计目的:栈机制是一种高度节律性控制机制,面向对象编程却要求对象之间有方向、有目的的控制传动,从一开始,异常就是冲着改变程序控制结构,以适应面向对象程序更有效地工作这个主题,而不是仅为了进行错误处理。

48、异常的处理机制:传统的错误处理机制是通过返回值来处理错误,异常的处理机制:1)C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。2)异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试。异常的超脱式函数机制决定了其对函数跨域式会跳,跨越函数。

49、异常处理语法:1)若有异常则通过throw操作创建一个异常对象并抛掷。2)将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句,然后执行try块内的保护段。3)如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。4)catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常(或继续抛掷异常)。5)如果匹配的处理器未找到,则运行函数terminate将被自动调用,其缺省功能是调用abort终止程序。6)处理不了的异常,可以在catch的最后一个分支,使用throw语法向上抛。

50、栈解旋:异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋(unwinding)。为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类
型,如void func() throw (A, B, C , D); 这个函数能够且只能抛出类型A B C D及其子类型的异常。如果在函数声明中没有包含异常接口声明,则函数可以抛掷任何类型的异常,如void func(); 一个不抛掷任何类型异常的函数可以声明为:void func() throw();如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexpected函数会被调用,该函数默认行为调用terminate函数中止程序。

51、throw的异常是有类型的,可以是数字、字符串、类对象,catch严格按照类型进行匹配。C++标准异常库继承关系:

每个类所在的头文件在图下方标识出来:标准异常类的成员:在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载;logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述;所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息。

52、输入输出流:程序的输入指的是从输入文件将数据传送给程序,程序的输出指的是从程序将数据传送给输出文件。C++输入输出包含以下三个方面的内容:对系统指定的标准设备的输入和输出。即从键盘输入数据,输出到显示器屏幕。这种输入输出称为标准的输入输出,简称标准I/O。以外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件。以外存文件为对象的输入输出称为文件的输入输出,简称文件I/O。对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(实际上可以利用该空间存储任何信息)。这种输入和输出称为字符串输入输出,简称串I/O。在C语言中,用prinV和scanf进行输入输出,往往不能保证所输入输出的数据是可靠的安全的。在C++的输入输出中,编译系统对数据类型进行严格的
检查,凡是类型不正确的数据都不可能通过编译。因此C++的I/O操作是类型安全(type safe)的。C++的I/O操作是可扩展的,不仅可以用来输入输出标准类型的数据,也可以用于用户自定义类型的数据。C++通过I/O类库来实现丰富的I/O功能。这样使C++的输人输出明显地优于C语言中的prinV和scanf,但是也为之付出了代价,C++的I/O系统变得比较复杂,要掌握许多细节。C++编译系统提供了用于输入输出的iostream类库。iostream这个单词是由3个部分组成的,即i-­o-­stream意为输入输出流。在iostream类库中包含许多用于输入输出的类。ios是抽象基类,由它派生出istream类和ostream类,两个类名中第1个字母i和o分别代表输入(input)和输出(output)。istream类支持输入操作,ostream类支持输出操作,iostream类支持输入输出操作。iostream类是从istream类和ostream类通过多重继承而派生的类。其继承层次见上图表示。C++对文件的输入输出需要用ifstrcam和ofstream类,两个类名中第1个字母i和o分别代表输入和输出,第2个字母f代表文件(file)。ifstream支持对文件的输入操作,ofstream支持对文件的输出操作。类ifstream继承了类istream,类ofstream继承了类ostream,类fstream继承了类iostream。iostream类库的接口分别由不同的头文件来实现。常用的有iostream 包含了对输入输出流进行操作所需的基本信息。 fstream 用于用户管理的文件的I/O操作。 strstream 用于字符串流I/O。stdiostream 用于混合使用C和C + +的I/O机制时,例如想将C程序转变为C++程序。 iomanip 在使用格式化I/O时应包含此头文件。

53、标准I/O流:标准I/O对象:cin,cout,cerr,clog; cout流对象cout是console output的缩写,意为在控制台(终端显示器)的输出。强调几点。1) cout不是C++预定义的关键字,它是ostream流类的对象,在iostream中定义。 顾名思义,流是流动的数据,cout流是流向显示器的数据。cout流中的数据是用流插入运算符“<<”顺序加入的。如果有cout<<"I "<<"study C++ "<<"very hard. << “wang bao ming ";按顺序将字符串"I ", "study C++ ", "very hard."插人到cout流中,cout就将它们送到显示器,在显示器上输出字符串"I study C++ very hard."。cout流是容纳数据的载体,它并不是一个运算符。人们关心的是cout流中的内容,也就是向显示器输出什么。2) 用“ccmt<<”输出基本类型的数据时,可以不必考虑数据是什么类型,系统会判断数据的类型,并根据其类型选择调用与之匹配的运算符重 载函数。这个过程都是自动的,用户不必干预。如果在C语言中用prinf函数输出不同类型的数据,必须分别指定相应的输出格式符,十分麻烦,而且容易出 错。C++的I/O机制对用户来说,显然是方便而安全的。3) cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当向cout流插 人一个endl时,不论缓冲区是否已满,都立即输出流中所有数据,然后插入一个换行符,并刷新流(清空缓冲区)。注意如果插人一个换行符”\n“(如cout<<a<<"\n"),则只输出和换行,而不刷新cout 流(但并不是所有编译系统都体现出这一区别)。4) 在iostream中只对"<<"和">>"运算符用于标准类型数据的输入输出进行了重载,但未对用户声明的类型数据的输入输出 进行重载。如果用户声明了新的类型,并希望用”<<"和">>"运算符对其进行输入输出,按照重运算符重载来做。

54、cerr流对象:cerr流对象是标准错误流,cerr流已被指定为与显示器关联。cerr的 作用是向标准错误设备(standard error device)输出有关出错信息。cerr与标准输出流cout的作用和用法差不多。但有一点不同:cout流通常是传送到显示器输出,但也可以被重定向 输出到磁盘文件,而cerr流中的信息只能在显示器输出。当调试程序时,往往不希望程序运行时的出错信息被送到其他文件,而要求在显示器上及时输出,这时 应该用cerr。cerr流中的信息是用户根据需要指定的。
clog流对象:clog流对象也是标准错误流,它是console log的缩写。它的作用和cerr相同,都是在终端显示器上显示出错信息。区别:cerr是不经过缓冲区,直接向显示器上输出有关信息,而clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++初学知识点总结 的相关文章

  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • 进程何时获得 SIGABRT(信号 6)?

    C 中进程获得 SIGABRT 的场景有哪些 该信号是否始终来自进程内部 或者该信号可以从一个进程发送到另一个进程吗 有没有办法识别哪个进程正在发送该信号 abort 向调用进程发送SIGABRT信号 就是这样abort 基本上有效 abo
  • ASP.NET MVC 中的经典 ASP (C#)

    我有一个应用程序想要 最终 转换为 ASP NET MVC 我想要进行全面的服务升级 到 ASP NET 但想要使用当前的 ASP 内容来运行当前的功能 这样我就可以在对新框架进行增量升级的同时升级小部分 该站点严重依赖于不太成熟的 VB6
  • 以编程方式检查页面是否需要基于 web.config 设置进行身份验证

    我想知道是否有一种方法可以检查页面是否需要基于 web config 设置进行身份验证 基本上如果有这样的节点
  • 如何在 Android NDK 中创建新的 NativeWindow 而无需 Android 操作系统源代码?

    我想编译一个 Android OpenGL 控制台应用程序 您可以直接从控制台启动 Android x86 运行 或者从 Android x86 GUI 内的 Android 终端应用程序运行 这个帖子 如何在 Android NDK 中创
  • 32 位应用程序的特征最大矩阵大小

    所以 我正在寻找Eigen http eigen tuxfamily org index php title Main Page当我尝试声明大于 10000x10000 的矩阵时 包崩溃 我需要声明一个像这样的矩阵 可靠地大约有 13000
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • 从多个类访问串行端口

    我正在尝试使用串行端口在 arduino 和 C 程序之间进行通信 我对 C 编程有点陌生 该程序有多种用户控制形式 每一个都需要访问串口来发送数据 我需要做的就是从每个类的主窗体中写入串行端口 我了解如何设置和写入串行端口 这是我的 Fo
  • ASP MVC:服务应该返回 IQueryable 的吗?

    你怎么认为 你的 DAO 应该返回一个 IQueryable 以便在你的控制器中使用它吗 不 您的控制器根本不应该处理任何复杂的逻辑 保持苗条身材 模型 而不是 DAO 应该将控制器返回给视图所需的所有内容 我认为在控制器类中看到查询 甚至
  • IronPython:没有名为 json 的模块

    我安装了 IronPython 我的 python 文件如下所示 import sys print sys version import json 运行它的代码 var p Python CreateEngine var scope p C
  • 如何从网站下载 .EXE 文件?

    我正在编写一个应用程序 需要从网站下载 exe 文件 我正在使用 Visual Studio Express 2008 我正在使用以下代码 private void button1 Click object sender EventArgs
  • 在 2D 中将一个点旋转另一个点

    我想知道当一个点相对于另一个点旋转一定角度时如何计算出新的坐标 我有一个块箭头 想要将其相对于箭头底部中间的点旋转角度 theta 这是允许我在两个屏幕控件之间绘制多边形所必需的 我无法使用和旋转图像 从我到目前为止所考虑的情况来看 使问题
  • 基于xsd模式生成xml(使用.NET)

    我想根据我的 xsd 架构 cap xsd 生成 xml 文件 我找到了这篇文章并按照说明进行操作 使用 XSD 文件生成 XML 文件 https stackoverflow com questions 6530424 generatin
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 剪贴板在 .NET 3.5 和 4 中的行为有所不同,但为什么呢?

    我们最近将一个非常大的项目从 NET Framework 3 5 升级到 4 最初一切似乎都工作正常 但现在复制粘贴操作开始出现错误 我已经成功制作了一个小型的可复制应用程序 它显示了 NET 3 5 和 4 中的不同行为 我还找到了一种解
  • 运算符“==”不能应用于“int”和“string”类型的操作数

    我正在编写一个程序 我想到了一个数字 然后计算机猜测了它 我一边尝试一边测试它 但我不断收到不应该出现的错误 错误是主题标题 我使用 Int Parse 来转换我的字符串 但我不知道为什么会收到错误 我知道它说 不能与整数一起使用 但我在网
  • 使用 CSharpCodeProvider 类编译 C# 7.3 的 C# 编译器版本是什么?

    我想使用 Microsoft CSharp CSharpCodeProvider 类来编译 C 7 3 代码 编译器版本在 IDictionary 中指定 在创建新的 CSharpCodeProvider 时将其作为输入 例如 Compil
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没
  • 使用 Crypto++ 获取 ECDSA 签名

    我必须使用 Crypto 在变量中获取 ECDSA 签名 我在启动 SignMessage 后尝试获取它 但签名为空 我怎样才能得到它 你看过 Crypto wiki 吗 上面有很多东西椭圆曲线数字签名算法 http www cryptop
  • 错误:无效使用不完整类型“类 Move”/未定义对 Move::NONE 的引用

    拜托 我不知道为什么这个简单的代码被拒绝 它给了我 2 个编译错误 请帮帮我 I use 代码 块 20 03 我的编译器是GNU GCC 移动 hpp class Move public Move Move int int public

随机推荐

  • 汽车 Automotive > T-BOX GNSS高精定位测试相关知识

    参考 https en wikipedia org wiki Global Positioning System GPS和GNSS的关系 GPS Global Positioning System 全球定位系统是美国军民两用的导航定位卫星系
  • rsa生成公私钥php,php中rsa生成公私钥和加解密

    php中rsa生成公私钥和加解密 注意 php使用RSA时需要开启openssl扩展 生成公私钥 创建公私钥 res openssl pkey new 获取私钥 openssl pkey export res private key 获取公
  • Python跨平台应用-BeeWare打造移动端应用和构建Android时的异常处理

    目录 简介 安装 创建demo 运行 打包为Windows程序 打包为安卓APP 构建安卓错误处理 gt gt gradle改为本地 报错1 Could not resolve all artifacts for configuration
  • cgo+gSoap+onvif学习总结:1、方案初衷、资料收集及cgo实现helloworld

    cgo gSoap onvif学习总结 1 方案初衷 资料收集及cgo实现helloworld 文章目录 cgo gSoap onvif学习总结 1 方案初衷 资料收集及cgo实现helloworld 1 前言 2 资料收集 3 cgo h
  • Keil转STM32CubeIDE工程移植问题记录

    Keil转STM32CubeIDE工程移植问题记录 1 编译报错问题处理 2 工程相关配置问题 3 调试器配置 从Keil软件转战STM32CubeIDE 转换的过程中遇到了不少问题 在此记录一下 防止以后再踩坑 也给同样有转软件需求的朋友
  • 老派程序员——徒手实现伟大成就

    原文地址 http www csdn net article 2012 08 06 2808178 摘要 本文介绍了三位非常著名的程序员 Ken Thompson Joe Armstrong 和 Jamie Zawinski 他们是如何发明
  • Java中的Set集合接口实现插入对象不重复的原理

    java lang Object中对hashCode的约定 1 在一个应用程序执行期间 如果一个对象的equals方法做比较所用到的信息没有被修改的话 则对该对象调用hashCode方法多次 它必须始终如一地返回同一个整数 2 如果两个对象
  • litemall项目部署,启动后台前端cnpm install报错

    在命令窗口切换到C test file litemall litemall admin目录输入cnpm install 出现报错 1 将node modules 文件删除 2 在命令窗口切换到C test file litemall lit
  • 局域网下远程唤醒主机

    Linux下远程唤醒 Linux下唤醒远程主机使用的命令主要是 wakeonlen 安装 apt get install wakeonlen 使用命令为 wakeonlen AC 48 11 Windows下远程唤醒 Windows下主要的
  • 配置Nginx以隐藏访问端口

    进入usr local nginx conf 编辑nginx conf文件 在http模块中加入下句 include vhost conf 进入usr local nginx conf vhost xxx conf 编写如下内容 nginx
  • Maven依赖仓库

    Maven依赖仓库
  • Linux的目录切换和用户管理

    切换目录 在使用linux系统的时候 会用cd来切换目录 cd 切换到根目录 cd 切换到主目录 cd 切换到之前工作目录 cd 虽然很方便但只能保存一次目录 pushd命令使用目录堆栈可以把多个目录存放起来 配套使用pushd popd
  • android实现共享数据

    计划是在后台开个service定位服务 前台需要经纬度的时候 从service获取 实现过程如下 public GPSService minTime UnTaskCheckingActivity minTime minDistance Un
  • mpc模型预测控制从原理到代码实现 mpc模型预测控制详细原理推导 matlab和c++两种编程实现

    mpc模型预测控制从原理到代码实现 mpc模型预测控制详细原理推导 matlab和c 两种编程实现 四个实际控制工程案例 双积分控制系统 倒立摆控制系统 车辆运动学跟踪控制系统 车辆动力学跟踪控制系统 包含上述所有的文档和代码 ID 564
  • 接口自动化平台(三):Promise简介 + 前端 + 后端 + 联调

    目录 1 Promise 2 接口自动化平台前端开发 2 1 前端环境搭建 2 2 新建用例页 CreateCase 2 2 1 增加路由信息 config routes ts 2 2 2 增加对应后端接口的信息 config proxy
  • 启动项

    size medium 1 imjpmig exe是微软Microsoft输入法编辑器程序 是微软Microsoft输入法编辑器程序 我给禁用了 每什么影响似乎 2 ctfmon exe是Microsoft Office产品套装的一部分 提
  • Docker运行MySQL5.7

    步骤如下 1 获取镜像 docker pull mysql 5 7 2 创建挂载目录 mkdir home mydata data mkdir home mydata log mkdir home mydata conf 3 先启动dock
  • FreeRTOS笔记(二)

    FreeRTOS笔记 二 静态任务 文章目录 FreeRTOS笔记 二 静态任务 一 任务定义 二 任务创建 2 1 定义任务栈 2 2 定义任务函数 2 3 定义任务控制块 2 4 实现任务创建函数 三 实现就绪列表 3 1 定义就绪列表
  • PCL去除地面

    如图所示 分别是 原图 gt 直通滤波后 gt 取地面的图 gt 取地面的凹凸四边行加地面上的物体图
  • C++初学知识点总结

    C 学习笔记 1 namespace 所谓的namespace就是指标识符的各种可见范围 C 标准程序中的所有标识符都被定义于一个名为std的namespace中 2 iostream与iostream h的差别 差别当然不只是一个带后缀