C++面试宝典(1)

main.cpp

//#include尖括号和双引号的区别
//1)#include  <> ,认为该头文件是标准头文件。编译器将会在预定义的位置集查找该头文件, \
这些预定义的位置可以通过设置查找路径环境变量或者通过命令行选项来修改。使用的查找方式因编译器的不同而差别迥异。从标准库路径寻找
//2)#include "",认为它是非系统头文件,非系统头文件的查找通常开始于源文件所在的路径。查找范围大于<>。从当前工作路径寻找

#include<iostream>
#include<string>
#include "cfunction.h"
#include "cppfunction.h"
using namespace std;

//C++的内存管理方式:
//在c++中内存主要分为5个存储区:
//栈(Stack):局部变量,函数参数等存储在该区,由编译器自动分配和释放.栈属于计算机系统的数据结构,进栈出栈有相应的计算机指令支持,而且分配专门的寄存器存储栈的地址,效率分高,内存空间是连续的,但栈的内存空间有限。
//堆(Heap):需要程序员手动分配和释放(new, delete),属于动态分配方式。内存空间几乎没有限制,内存空间不连续,因此会产生内存碎片。操作系统有一个记录空间内存的链表,当收到内存申请时遍历链表,找到第一个空间大于申请空间的堆节点,将该节点分配给程序,并将该节点从链表中删除。一般,系统会在该内存空间的首地址处记录本次分配的内存大小,用于delete释放该内存空间。
//全局 / 静态存储区:全局变量,静态变量分配到该区,到程序结束时自动释放,包括DATA段(全局初始化区)与BSS段(全局未初始化段)。其中,初始化的全局变量和静态变量存放在DATA段,未初始化的全局变量和静态变量存放在BSS段。BSS段特点:在程序执行前BSS段自动清零,所以未初始化的全局变量和静态变量在程序执行前已经成为0.
//文字常量区:存放常量,而且不允许修改。程序结束后由系统释放。
//程序代码区:存放程序的二进制代码

//变量
//全局变量global variable \
定义在任何函数之外,可以被任一模块使用,在整个程序执行期间保持有效。 \
当几个函数要共享同一数据时全局变量将十分有效,但是使用全局变量是有一定弊端的: \
全局变量将在整个程序执行期间占有执行空间,即使它只在少数时间被用到; \
大量使用全局变量将导致程序混乱,特别是在程序较复杂时可能引起错误。
//局部变量local variable \
定义在函数内部的变量。局部变量只在定义它的模块内部起作用,当该段代码结束,这个变量就不存在了。 \
也就是说一个局部变量的生命期就是它所在的代码块的执行期, \
而当这段代码再次被执行时该局部变量将重新被初始化而不会保持上一次的值 \
(如果定义为静态变量,再次调用时将不会不会出事化而保持上次的值)。 \
需要注意的是,如果主程序和它的一个函数有重名的变量,当函数被调用时这个变量名只代表当前函数中的变量,而不会影响主程序中的同名变量。
//自动变量automatic variable \
由auto修饰,动态分配存储空间,存储在动态存储区中,对他们分配和释放存储空间的工作是由编译系统自动处理的。
//寄存器变量register variable \
存储在运算器中的寄存器里的变量,可提高执行效率。
//静态变量static variable \
由连接器分配在静态内存中的变量。
//外部变量external variable \
由extern修饰的变量

//变量的生存期
//静态生存期:只要程序开始运行,这种生存期的变量就被分配了内存,这种变量的生存期和程序的运行期相同。static
//局部生存期:这种变量的生存期开始于程序执行经过其声明点时,而结束于命名它的标识符所在的作用域尾。函数内部定义的变量
//动态生存期:这种变量可以随时创建,随时删除,创建和删除是程序员用内存操作函数进行的。new

//定义和声明的区别
//声明是告诉编译器变量的类型和名字,不会为变量分配空间
//定义需要分配空间,同一个变量可以被声明多次,但是只能被定义一次

//extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
extern int i; // 声明i 而非定义i。extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
int j; // 声明并定义。全局变量。
extern double pi = 3.1415926; // 定义,抵消了声明。这个时候相当于没有extern
//如果想多个文件中使用同一个变量,就必须将声明和定义分开。
//此时,变量的定义只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

//隐式转换:编译器根据需要自动转换变量类型。
double d = 82.0;
int i = d;
//显示转换:也称为强制类型转换,要定义类型转换成要用的值的类型。
int *ip;
char* pc = (char*)ip;
//C++有四大强制类型转换符:reinterpret_cast, const_cast, static_cast, dynamic_cast.使用形式为:cast-name<type>(expression) 

//什么是指针?
//指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。 \
指针变量声明的一般形式为:type *var-name;
//数组指针和指针数组的区别 
//数组指针(也称行指针) \
定义 int (*p)[n]; \
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 \
如要将二维数组赋给一指针,应这样赋值: \
int a[3][4]; \
int(*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 \
p = a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] \
p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][] \
所以数组指针也称指向一维数组的指针,亦称行指针。 
//指针数组 \
定义 int *p[n]; \
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。 \
这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a; \
因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。 \
但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
//优先级:()>[]>*

//简述数组与指针的区别
//数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
//(1)修改内容上的差别
//char a[] = “hello”;
//a[0] = ‘X’;
//char *p = “world”; // 注意p 指向常量字符串
//p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
//(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p), p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++ / C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
//char a[] = "hello world";
//char *p = a;
//cout << sizeof(a) << endl; // 12 字节
//cout << sizeof(p) << endl; // 4 字节
//计算数组和指针的内存容量
//void Func(char a[100])
//{
//	cout << sizeof(a) << endl; // 4 字节而不是100 字节
//}

//函数指针和指针函数用法和区别(定义不同,写法不同,用法不同)
//指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。声明格式为:*类型标识符 函数名(参数表).int *fun(int x,int y);
//函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。声明格式:类型说明符 (*函数名) (参数).int (*fun)(int x,int y); \
int add(int x,int y){return x + y;} \
int sub(int x,int y){return x - y;} \
int (*fun)(int x,int y); \
fun = add;(*fun)(1,2); \
fun = &sub;(*fun)(5,3);fun(5,3);


//const与#define 相比,有何优点
//const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
//1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
//2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

//#define和const的区别
//1)#define定义的常量没有类型,所给出的是一个立即数;const定义的常量有类型名字,存放在静态区域
//2)处理阶段不同,#define定义的宏变量在预处理时进行替换,可能有多个拷贝,const所定义的变量在编译时确定其值,只有一个拷贝。
//3)#define定义的常量是不可以用指针去指向,const定义的常量可以用指针去指向该常量的地址
//4)#define可以定义简单的函数,const不可以定义函数



//结构与联合的区别
//(1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 
//(2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
//结构体,类的一种,其成员默认为public型。在C语言中,结构体不能包含函数。
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c,但没有标明其标签,声明了结构体变量s1
struct
{
	int a;
	char b;
	double c;
} s1;

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c,结构体的标签被命名为SIMPLE,用SIMPLE标签的结构体,另外声明了变量t1, t2[20], *t3
struct SIMPLE
{
	int a;
	char b;
	double c;
};
SIMPLE t1, t2[20], *t3;

//可以用typedef创建新类型,此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c,结构体的标签被命名为Simple2,用Simple2作为类型声明新的结构体变量u1, u2[20], *u3
typedef struct
{
	int a;
	char b;
	double c;
} Simple2;
Simple2 u1, u2[20], *u3;//若去掉typedef则编译报错,error C2371: “Simple2”: 重定义;不同的基类型
//注:在上面的声明中,第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的,如果令t3 = &s1,则是非法的。
//注:在C语言中,结构体不能包含函数。
//注:在C++中,结构体可以包含函数。

//1、利用结构定义变量时,不需要带上关键字struct。例如:Student std;
//2、允许在struct中定义成员函数。默认访问权限为public
//3、在struct中没显定义任何构造函数,那可以用{}进行初始化,如果定义了构造函数,则必须用构造函数的形式初始化。

//C++中的结构体与类的区别: \
(1)class中默认的成员访问权限是private的,而struct中则是public的。 \
(2)class继承默认是private继承,而从struct继承默认是public继承。
struct Student {
	int age;
	int score;
	Student(int a, int s) {
		age = a;
		score = s;
	}
};
//结构体的作用
//1.在实际项目中,结构体是大量存在的。研发人员常使用结构体来封装一些属性来组成新的类型。由于C语言内部程序比较简单,研发人员通常使用结构体创造新的“属性”,其目的是简化运算。
//2.结构体在函数中的作用不是简便,最主要的作用就是封装。封装的好处就是可以再次利用。让使用者不必关心这个是什么,只要根据定义使用就可以了。

//结构体的大小与内存对齐
//默认的对齐方式:各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。 \
同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
//注:VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下, \
VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
struct MyStruct
{
	double dda1;
	char dda;
	int type;
};
//错:sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13。
//对:当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。
struct MyStruct1
{
	char dda;
	double dda1;
	int type;
};
//错:sizeof(MyStruct1)=sizeof(double)+sizeof(char)+sizeof(int)=13。
//对:当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct1)为24。

//联合体。多种变量共用一个存储空间,已达到节省空间的作用。它由较大的类型(int)决定。
union testunion {
	char c;
	int i;
};


void printInfo(Employee& emp);//函数声明。如果没有这个声明,则在此函数实现前的位置调用,会发生错误

//在函数前加上关键字inline说明了一个内联函数,这使一个函数在程序行里进行代码扩展而不被调用。 \
这样的好处是减少了函数调用的开销,产生较快的执行速度。但是由于重复编码会产生较长代码,所以内联函数通常都非常小。 \
如果一个函数在类说明中定义,则将自动转换成内联函数而无需用inline说明。
//注:类内声明可以不用加上inline关键字,但是类外定义函数体时必须要加上,这样才能保证编译器能够识别其为内联函数。
inline int A(int x) { return 2 * x; }
//使用内联函数的时候要注意:
//1.递归函数不能定义为内联函数
//2.内联函数一般适合于不存在while和switch等复杂的结构且只有1~5条语句的小函数上,否则编译系统将该函数视为普通函数。
//3.内联函数只能先定义后使用,否则编译系统也会把它认为是普通函数。
//4.对内联函数不能进行异常的接口声明。

//为什么inline能取代宏?
//1、 inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高。
//2、 很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。
//3、 inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。

//内联函数和宏的区别?
//内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。 \
而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。 \
你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。内联函数与带参数的宏定义进行下比较,它们的代码效率是一样,但是内联欢函数要优于宏定义,因为内联函数遵循的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关上内联扩展,将与一般函数一样进行调用,比较方便。


void printInfo(Employee& emp/*形参*/)
{
	emp.GetPay();
	emp.Show();
}
void test03()
{
	//Employee* p = new Employee;//错误,抽象类不允许创建对象
	//new/delete不是库函数
	Employee* p = new Manager;//new会调用构造函数。指向基类的指针p,基类的析构函数不会调用子类的析构函数。堆区
	//构造顺序:基类->成员->子类
	delete p;//delete会调用对象的析构函数。delete一个指向子类对象的基类指针,将可能引发内存泄漏
	//析构顺序:子类->成员->基类
	Manager m1("xiao1"/*实参*/);
	//double mSalary = m1.base + m1.ticheng; //错误,私有成员不能直接调用
	Technician t1("xiao2","engineer", 200);
	Technician t2 = t1;//拷贝构造函数
	Technician t3;
	t3 = t2;//调用重载运算符
	t2.setName("xiao22");
	SalesMan s1("xiao3", 80000);
	SalesMan s2("xiao4", 10000);

	SalesManager sm1("xiao5");

	printInfo(m1);
	printInfo(t1);
	printInfo(t2);
	printInfo(s1);
	printInfo(s2);
	printInfo(sm1);

	FriendSalesMan fs;
	fs.Print(s1);

	printf("Total People:%d",Employee::getNum());//静态函数只要使用类名加范围解析运算符 :: 就可以访问。
	//C++支持两种初始化形式:
	//拷贝初始化 int a = 5; 和直接初始化 int a(5);  \
	对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数,也就是说: \
	A x(2);  //直接初始化,调用构造函数 \
	A y = x;  //拷贝初始化,调用拷贝构造函数
}

int main()
{
	test03();

	system("pause");
	return EXIT_SUCCESS;
}

//递归函数 求累积和
//递归函数即自己调用自己的函数,写递归函数有两个条件
//1)递归的边界条件(递归结束条件)
//2)递归公式
int GetSum(int x)
{
	int z = 0;
	if (x <= 0)
	{
		cout << "Data Error" << endl;
	}
	if (1 == x) //递归的边界条件	
	{
		z = 1;
	}
	else if (x > 1) //递归调用	
	{
		z = x + GetSum(x - 1); //递归公式	
	}
	return z;
}


//内存分配方式以及它们的区别
//1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
//2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
//3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

//栈内存与文字常量区
//char str1[] = "abc";
//char str2[] = "abc";
//const char str3[] = "abc";
//const char str4[] = "abc";
//const char *str5 = "abc";
//const char *str6 = "abc";
//char *str7 = "abc";
//char *str8 = "abc";
//cout << (str1 == str2) << endl;//0  分别指向各自的栈内存
//cout << (str3 == str4) << endl;//0  分别指向各自的栈内存
//cout << (str5 == str6) << endl;//1指向文字常量区地址相同
//cout << (str7 == str8) << endl;//1指向文字常量区地址相同
//结果是:0 0 1 1
//解答:str1, str2, str3, str4是数组变量,它们有各自的内存空间;而str5, str6, str7, str8是指针,它们指向相同的常量区域。

//静态绑定和动态绑定的介绍
//静态绑定和动态绑定是C++多态性的一种特性
//1)对象的静态类型和动态类型
//静态类型:对象在声明时采用的类型,在编译时确定
//动态类型:当前对象所指的类型,在运行期决定,对象的动态类型可变,静态类型无法更改
//2)静态绑定和动态绑定
//静态绑定:绑定的是对象的静态类型,函数依赖于对象的静态类型,在编译期确定
//动态绑定:绑定的是对象的动态类型,函数依赖于对象的动态类型,在运行期确定
//只有虚函数才使用的是动态绑定,其他的全部是静态绑定

//引用是否能实现动态绑定,为什么引用可以实现
//可以。因为引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指的对象的实际类型所定义的。

//深拷贝和浅拷贝的区别
//深拷贝和浅拷贝可以简单的理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,如果资源重新分配了就是深拷贝;反之没有重新分配资源,就是浅拷贝。

//什么情况下会调用拷贝构造函数(三种情况) 
//系统自动生成的构造函数:普通构造函数和拷贝构造函数 (在没有定义对应的构造函数的时候)
//生成一个实例化的对象会调用一次普通构造函数,而用一个对象去实例化一个新的对象所调用的就是拷贝构造函数
//调用拷贝构造函数的情形:
//1)用类的一个对象去初始化另一个对象的时候
//2)当函数的参数是类的对象时,就是值传递的时候,如果是引用传递则不会调用
//3)当函数的返回值是类的对象或者引用的时候
//#include <iostream>
//#include <string> 
//using namespace std; 
//class A{	
//private:		
//int data;	
//public:		
//	A(int i){ data = i;} 	//自定义的构造函数		
//	A(A && a);  			//拷贝构造函数 		
//	int getdata(){return data;} };//拷贝构造函数 
//A::A(A && a){	
//	data = a.data;	
//	cout <<"拷贝构造函数执行完毕"<<endl;}//参数是对象,值传递,调用拷贝构造函数
//int getdata1(A a){	return a.getdata();}//参数是引用,引用传递,不调用拷贝构造函数 
//int getdata2(A &a){	return a.getdata();} //返回值是对象类型,会调用拷贝构造函数 
//A getA1(){ 	A a(0); 	return a; }  //返回值是引用类型,会调用拷贝构造函数,因为函数体内生成的对象是临时的,离开函数就消失 
//A& getA2(){ 	A a(0); 	return a; }   
//int main(){    
//	A a1(1);      A b1(a1);           		//用a1初始化b1,调用拷贝构造函数      
//	A c1=a1;            		//用a1初始化c1,调用拷贝构造函数        
//	int i=getdata1(a1);        	//函数形参是类的对象,调用拷贝构造函数      
//	int j=getdata2(a1);      	//函数形参类型是引用,不调用拷贝构造函数        
//	A d1=getA1();       		//调用拷贝构造函数      
//	A e1=getA2();     			//调用拷贝构造函数        
//	return 0;  }  

//typdef和define区别
//#define是预处理命令,在预处理是执行简单的替换,不做正确性的检查
//typedef是在编译时处理的,它是在自己的作用域内给已经存在的类型一个别名
//typedef    (int*)pINT;
//#define    pINT2   int*
//效果相同?实则不同!实践中见差别:pINT a, b; 的效果同int *a; int *b; 表示定义了两个整型指针变量。而pINT2 a, b; 的效果同int *a, b; 表示定义了一个整型指针变量a和整型变量b。

//volatile关键字在程序设计中有什么作用
//volatile是“易变的”、“不稳定”的意思。volatile是C的一个较为少用的关键字,它用来解决变量在“共享”环境下容易出现读取错误的问题。


//内联函数
//指用inline关键字修饰的函数。
//在函数前加上关键字inline说明了一个内联函数,这使一个函数在程序行里进行代码扩展而不被调用。这样的好处是减少了函数调用的开销,产生较快的执行速度。但是由于重复编码会产生较长代码,所以内联函数通常都非常小。如果一个函数在类说明中定义,则将自动转换成内联函数而无需用inline说明。

//假设有如下函数声明:
//string foo();
//void bar(string & s);
//那么下面的表达式将是非法的:
//bar(foo());
//bar("hello world");
//原因在于foo()和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
//引用型参数应该在能被定义为const的情况下,尽量定义为const 。

//友元
//类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。 \
非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。 \
另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
//为了解决上述问题,提出一种使用友元的方案。 \
友元是一种定义在类外部的普通函数或类,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。 \
友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率, \
但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。 \
不过,类的访问权限确实在某些应用场合显得有些呆板,从而容忍了友元这一特别语法现象。
//友元函数 \
特点:友元函数是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。 \
友元关系不具对称性。即 A 是 B 的友元,但 B 不一定是 A 的友元。 友元关系不具传递性。即 B 是 A 的友元,C 是 B 的友元,但是 C 不一定是 A 的友元。
//class Point
//{
//public:
//	Point(double xx, double yy)
//	{
//		x = xx;
//		y = yy;
//	};
//	void Getxy();
//	friend double Distance(Point &a, Point &b);
//private:
//	double x, y;
//};
//void Point::Getxy()
//{
//	cout << "(" << x << "," << y << ")" << endl;
//}
//double Distance(Point &a, Point &b)
//{
//	double dx = a.x - b.x;
//	double dy = a.y - b.y;
//	return sqrt(dx*dx + dy * dy);
//}
//int main(void)
//{
//	Point p1(3.0, 4.0), p2(6.0, 8.0);
//	p1.Getxy();
//	p2.Getxy();
//	double d = Distance(p1, p2);
//	cout << "Distance is" << d << endl;
//	return 0;
//}
//在该程序中的Point类中说明了一个友元函数Distance(),它在说明时前边加friend关键字,标识它不是成员函数,而是友元函数。 \
它的定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。 \
但是,它可以引用类中的私有成员,函数体中a.x,b.x,a.y,b.y都是类的私有成员,它们是通过对象引用的。 \
在调用友元函数时,也是同普通函数的调用一样,不要像成员函数那样调用。 \
本例中,p1.Getxy()和p2.Getxy()这是成员函数的调用,要用对象来表示。 \
而Distance(p1, p2)是友元函数的调用,它直接调用,不需要对象表示,它的参数是对象。(该程序的功能是已知两点坐标,求出两点的距离。)
//友元类
//友元除了函数以外,还可以是类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
//注意事项
//(1) 友元关系不能被继承。
//(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
//(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。
//以下语句说明类B是类A的友元类:
//class A
//{
//	…
//public:
//	friend class B;
//	…
//};
//经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。

//C++ STL基本容器使用 
//关联容器和顺序容器
//c++中有两种类型的容器:顺序容器和关联容器,顺序容器主要有:vector、list、deque等。 \
其中vector表示一段连续的内存地址,基于数组的实现,list表示非连续的内存,基于链表实现。 \
deque与vector类似,但是对于首元素提供删除和插入的双向支持。 \
关联容器主要有map和set。map是key-value形式的,set是单值。 \
map和set只能存放唯一的key值,multimap和multiset可以存放多个相同的key值。 \
容器类自动申请和释放内存,我们无需new和delete操作。

//C++ 模板
//模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
//模板是一种对类型进行参数化的工具;
//通常有两种形式:函数模板和类模板;
//函数模板针对仅参数类型不同的函数;
//#include <iostream>
//#include <string>
//
//using namespace std;
//
//template <typename T>
//inline T const& Max(T const& a, T const& b)
//{
//	return a < b ? b : a;
//}
//int main()
//{
//
//	int i = 39;
//	int j = 20;
//	cout << "Max(i, j): " << Max(i, j) << endl;
//
//	double f1 = 13.5;
//	double f2 = 20.7;
//	cout << "Max(f1, f2): " << Max(f1, f2) << endl;
//
//	string s1 = "Hello";
//	string s2 = "World";
//	cout << "Max(s1, s2): " << Max(s1, s2) << endl;
//
//	return 0;
//}
//类模板针对仅数据成员和成员函数类型不同的类。
//#include <iostream>
//#include <vector>
//#include <cstdlib>
//#include <string>
//#include <stdexcept>
//
//using namespace std;
//
//template <class T>
//class Stack {
//private:
//	vector<T> elems;     // 元素 
//
//public:
//	void push(T const&);  // 入栈
//	void pop();               // 出栈
//	T top() const;            // 返回栈顶元素
//	bool empty() const {       // 如果为空则返回真。
//		return elems.empty();
//	}
//};
//
//template <class T>
//void Stack<T>::push(T const& elem)
//{
//	// 追加传入元素的副本
//	elems.push_back(elem);
//}
//
//template <class T>
//void Stack<T>::pop()
//{
//	if (elems.empty()) {
//		throw out_of_range("Stack<>::pop(): empty stack");
//	}
//	// 删除最后一个元素
//	elems.pop_back();
//}
//
//template <class T>
//T Stack<T>::top() const
//{
//	if (elems.empty()) {
//		throw out_of_range("Stack<>::top(): empty stack");
//	}
//	// 返回最后一个元素的副本 
//	return elems.back();
//}
//
//int main()
//{
//	try {
//		Stack<int>         intStack;  // int 类型的栈 
//		Stack<string> stringStack;    // string 类型的栈 
//
//		// 操作 int 类型的栈 
//		intStack.push(7);
//		cout << intStack.top() << endl;
//
//		// 操作 string 类型的栈 
//		stringStack.push("hello");
//		cout << stringStack.top() << std::endl;
//		stringStack.pop();
//		stringStack.pop();
//	}
//	catch (exception const& ex) {
//		cerr << "Exception: " << ex.what() << endl;
//		return -1;
//	}
//}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值