/**
* 描述:C++学习笔记,测试程序通过总开关的形式,每次只能打开一个宏。
* KUI 20170924
**/
/*
描述:C++: 保留运行效率,提高开发效率(代码的复用)。
1. 完全兼容C语言。
2. 提供了更多特性(类,重载,继承,多态、异常),引入了对象的编程思想。
3. 提供标准模板库STL(常用数据结构和算法的集合:容器,迭代器,算法)。
*/
/**
第1课:C到C++的升级
1. 关键字加强:register,const
2. c语言const属性
方法:把类型int去掉,若const修饰p,则p不可变。若const修饰*p,则*p不可变
2.1 const int *p; //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变
2.2 int const *p; //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变
2.3 int *const p; //const 修饰 p, p 不可变, p 指向的对象可变
2.4 const int *const p; //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象都不可变
**/
//#define LESSON1_1_variable_init //1.1 变量初始化:用到时再定义,如for定义的变量,作用域只在for循环体内。
//#define LESSON1_2_register //1.2 register寄存器:register变量可以取址,C++编译器自动优化退化为普通变量。
//#define LESSON1_3_global_variable //1.3 全局变量:不能重复定义,拒绝二义性。
//#define LESSON1_4_const //1.4 const :真正的常量,编译器让其进入符号表,故不为该只读变量分配内存。
//#define LESSON1_5_struct_new_type //1.5 结构体:是新类型,不需要加上struct,直接:结构体名 变量名
//#define LESSON1_6_func_param_return //1.6 函数:C++要有确定的参数,确定的返回值
//#define LESSON1_7_example1_1_const_array //1.7 const:数组参数:const常量编译阶段处理的,可以作为数组参数,变量是运行时才确定的,不可以作为数组参数。
//#define LESSON1_8_example1_2_const_micro //1.8 const:宏:作用域
/**
第2课:C++中的引用
1. 真正的布尔类型bool
2. 引用:变量的别名
**/
//#define LESSON2_1_bool //2.1 布尔类型:true:非0, false:0,真正的
//#define LESSON2_3_three_eyes //2.3 三目运算符:返回值可以作为左值
//#define LESSON2_4_yinyong //2.4 引用:变量的别名
//#define LESSON2_5_yinyong //2.5 引用:代替指针:更好的可读性,实用性
//#define LESSON2_6_yinyong_const //2.6 引用:const结合, const 声明引用=变量
//#define LESSON2_7_yinyong_const //2.7 引用:const结合, const 声明引用=数值
//#define LESSON2_8_yinyong_storage_space //2.8 引用: 引用等效于常指针 int* const a; 地址不能改变
//#define LESSON2_example2_1 //2.9 引用:引用作为返回值,需要考虑作用域
/**
第3课:函数的升级
1. 内联函数
2. 函数占位符
//添加环境变量:C:\Program Files (x86)\Dev-Cpp\MinGW32\bin
//预处理:prepressing: armcpp/g++ -E main.cpp -o main.i //只是替换:1.#define 删除,展开宏, 2. 处理条件预编译指令#if #ifdef,3.删除注释
//预处理+编译:compilation: armcpp/g++ -S main.cpp -o main.s //c代码变为汇编代码: 1.词法分析,语法分析。
//预处理+编译+汇编:assembly: armcpp/g++ -c mian.cpp -o main.o //c代码变为目标文件.o(二进制) :1.把汇编指令翻译为机器指令。
//预处理+编译+汇编+链接:linking: armcpp/g++ main.cpp -o main //c文件变为可执行文件(二进制) :1.链接库文件。
**/
//#define LESSON3_1_inline_micro //3.1 内联函数:比函数少开销,比宏安全。
//#define LESSON3_example3_1 //3.2 内联函数:不一定申请成功
//#define LESSON3_2_func_default_param //3.3 函数参数:声明时指定默认参数
//#define LESSON3_3_func_default_param //3.3 函数参数:声明时指定默认参数
//#define LESSON3_4_set_position //3.4 函数:占位
//#define LESSON3_5_set_position //3.5 函数:占位+默认参数结合
/**
第4课:函数的升级
描述:函数重载:函数名+参数列表
1. 重载函数本质是相互独立的不同函数
2. 函数参数类型不同:2.1 参数个数不同,2.2 参数类型不同,2.3 参数顺序不同
3. 返回值不能作为重载的根据
**/
//#define LESSON4_1_func_overload //4.1 函数重载:即多态(批注:不是多态),一个函数名搭配不同参数
//#define LESSON4_2_func_overload //4.2 函数重载:即多态(批注:不是多态),一个函数名搭配不同参数
//#define LESSON4_3_func_overload_default_param //4.3 函数重载:重载+默认参数,有二义性不要同时使用
//#define LESSON4_4_func_overload_func_point //4.4 函数重载:函数指针类型必须匹配
//#define LESSON4_5_cplusplus_extern_c //4.5 c++编译器以c语言方式编译代码: extern "C" {}
//#define LESSON4_6_cplusplus_extern_c //4.6 C++与C语言相互调用:c++编译器以c语言方式编译代码: extern "C" {}
//#define LESSON4_example4_1_1 //4.7 C++与C语言相互调用:c++编译器以c语言方式编译代码: extern "C" {}
/**
第5课:新的关键字
描述:new, delete, namespace,xxx_case
关键字new 与malloc函数区别:
1. 关键字new是c++的一部分,malloc函数是c库提供的函数
2. new以类型为单元,malloc以字节为单元进行内存分配
3. new申请类型变量可以进行初始化,malloc不可以。
命名空间使用:
1. 使用整个命名看空间:using namespace name;
2. 使用命名空间的变量:using name::variable;
3.使用默认命名看空间变量:::variable;
难以解决的bug思路:
1. 运算符优先级:
2. 多线程编程,各线程之间的交互
3. 强制类型转换
**/
//#define LESSON5_1_keyword_new_delete //5.1 关键字:new delete 动态分配内存,与mallo有区别
//#define LESSON5_2_keyword_new_delete_init //5.2 关键字:new delete 初始化
//#define LESSON5_3_keyword_namespace //5.3 关键字:namespace 命名空间解决标识符冲突问题。
//#define LESSON5_4_keyword_namespace //5.4 关键字:namespace 命名空间的使用。
//#define LESSON5_5_type_conversion //5.5 C强制类型转换: c语言可任意类型进行转换,简单灵活,但是过于粗暴
//#define LESSON5_6_type_conversion_static_cast //5.6 C++强制类型转换:static_cast基本数据类型之间转换,不能用于指针。
//#define LESSON5_7_type_conversion_const_cast //5.7 C++强制类型转换:const_cast去除变量const属性
//#define LESSON5_8_type_conversion_reinterpret_cast //5.8 C++强制类型转换:reinterpret_cast指针之间强制类型转换
//#define LESSON5_9_type_conversion_dynamic_cast //5.9 C++强制类型转换:dynamic_cast 类之间强制类型转换
/**
第6课:专题一经典问题解析
描述:
1. 函数重载: 本质上是不同的函数:
2. extern "C"{}:c方式编译主要是对函数名进行编译。
3. const注意事项
const注意事项
1. 符号表为编译器内部过程的东西,不会进入程序存储空间。
2. 用字面量初始化的const常量才会进入符号表。如:const int value = 1;//符号表。 const int value = x; //分配内存
3. volatile修饰的const常量不会进入符号表,退化为只读变量。如volatile const value = 1;
4. const 引用于初始化变量类型相同,初始化变量分配空间。如:const char c = 1, const char& rc = c; //为c分配空间
5. const 引用于初始化变量类型不同,生成新的只读变量,独立空间。如:const char c = 1; const int& i = c;//新空间
引用注意事项:
1. 引用定义时必须初始化
2. 引用是变量的别名,与变量同生死。从此,引用的值是变量的值,地址是变量的地址。
3. 引用在c++用常量指针表示的。int const *p; //指针地址不可以修改。 可以理解为内部处理的东西。
函数重载注意事项:
1. 本质上是不同的函数。
2. c++编译出来: __Z4funcii
3. c编译出来:_func
深入理解extern "C" {}
1. extern "c"{} 告诉c++编译器对包含的代码进行c方式编译
2. c方式编译主要是对函数名进行编译。2.1 c++编译出来: __Z4funcii, 2.2 c编译出来:_func
3. 函数体编译还是以C++进行编译
4. 比较g++ -S main.cpp -main.s(加上extern "C"), g++ -S main.cpp -main.cpp.s(去掉 extern "C")可以知道唯一不同的是函数名。
**/
//#define LESSON6_example6_1_const //6.1 const:const注意事项
//#define LESSON6_example6_2_yinyong_storage_space //6.2 引用:别名,存储空间
//#define LESSON6_example6_3_func_overload //6.3 函数重载: 本质上是不同的函数:__Z4funcic, __Z4funcii
//#define LESSON6_example6_4_type_size //6.4 类型大小:
//#define LESSON6_example6_5_cplusplus_extern_c //6.5 extern "C"{}:c方式编译主要是对函数名进行编译。函数体编译还是以C++进行编译
/**
第7课:面向对象基本概念
1. 类:类是个抽象的概念(模板:比如人)
1.1 类:用于抽象描述一类事物所有属性和行为。
2. 对象: 对象是属于某个类的实体,一个具体存在的事物。(实例:比如KUI)
2.1 对象是一个具体的事物,拥有所属类的全部属性行为,并且每个属性都有特定的值。
1. struct 默认所有成员都是public。
2. 类组成:成员变量,成员函数。
3. 类权限:private,public,protected。
**/
//#define LESSON7_1_struct_to_object //7.1 struct表示类
/**
第8课:类中的封装
描述:
1. 类的精华在于封装: 将实现与接口分离。
2. 使用类时,不需要关心其实现细节。只需调用功能接口即可。(手机:只需知道如何发信息,拍照。最好的角色)
3. 创建一个类时,才需要考虑内部实现细节。 (手机:工程师设计软件细节,硬件细节)
**/
//#define LESSON8_example8_1_struct_member //8.1 类成员: 成员变量,成员函数。
//#define LESSON8_example8_2_struct_member //8.2 类成员作用域:类私有成员只能通过成员函数来访问。
//#define LESSON8_1_class_struct //8.3 struct class区别:struct 默认权限为public,class 默认权限为private,
//#define LESSON8_4_class_operator_test //8.4 操作类:类的精华在于封装: 将实现与接口分离。
/**
第9课:构造与析构函数上
描述:
1. 构造函数: 用于初始化
2. 默认:无参构造函数,拷贝构造函数。
析构函数:
1. 与类名相同的特殊成员函数:默认:无参构造函数,拷贝构造函数
2. 可以有参数,但是没有返回值。
3. 创建对象是自动调用
4. 可以重载
5. 用于初始化
注意:
1. 当类中没有定义任何一个构造函数,C++编译器会为提供无参构造函数和拷贝构造函数
2. 当类中定义了任意的非拷贝构造函数时,C++编译器不会为提供无参构造函数
**/
//#define LESSON9_1_object_init //9.1 类:显式初始化
//#define LESSON9_2_object_init //9.2 类:没有初始化,不确定数据
//#define LESSON9_3_object_constructor //9.3 构造函数: 用于初始化
//#define LESSON9_4_object_constructor //9.4 构造函数: 构造函数重载
//#define LESSON9_5_object_constructor //9.5 构造函数: 手动:无参构造函数,拷贝构造函数。
//#define LESSON9_6_object_constructor //9.6 构造函数: 自动:无参构造函数,拷贝构造函数。
//#define LESSON9_class_example_array //9.7 数组类:优化数组类拷贝构造函数
/**
第10课:构造与析构函数下
描述:
1. 析构函数:释放资源。
**/
//#define LESSON10_1_object_constructor_param_list //10.1 构造函数:参数列表:顺序无关,参数列表先调用,再调用构造函数
//#define LESSON10_2_object_destructor //10.2 析构函数:释放资源,没有参数,没有返回值
//#define LESSON10_3_object_destructor_array //10.3 析构函数:释放资源,数组类
//#define LESSON10_4_object_destructor_order //10.4 析构函数:析构函数调用顺序与构造函数调用顺序相反
//#define LESSON10_5_object_destructor_order //10.5 析构函数:直接调用构造函数,创建一个空白对象,很快就被销毁了
/**
第11课:类静态成员
描述:
1. 类静态成员:属于类,共享与对象。静态成员变量,静态成员函数,
2. this指针: this == &对象。对象其实就是结构体,对象指针就是结构体地址。通过->访问类成员(变量,函数)
类静态成员:
1. 类静态成员不需要创建对象即可访问,通过类名访问,有点像命名空间
2. 类静态成员共享与所有对象。
3. 类静态成员变量需要初始化分配空间。
对象大小:
1. 对象成员变量与成员函数是分开存储的
2. 普通成员变量:存储在对象中,与结构体struct相同
3. 静态成员变量:存储在全局数据区。
4. 成员函数:存储在代码段中。
5. 从用户的角度class是将属性+方法集成在同一个位置。
从计算机的角度,依然是数据段+程序段
//用户角度 //编译器角度
class Test struct Test
{ {
private: int mI;
int mI; };
public: void Test_initialize(Test *pThis, int i)
Test(int i) {
{ pThis->mI = i;
mI = i; }
} void Test_getI(Test *pThis)
{
int getI() return pThis->mI;
{ }
return mI;
}
void Test_Print()
static void Print() {
{ printf("This is class test.\n");
printf("This is class test.\n"); }
}
};
//用户角度 //编译器角度
Test a(10); Test a;
Test_initialize(&a, 10);
a.getI(); Test_getI(&a);
Test::Print(); Test_Print();
**/
//#define LESSON11_1_class_static //11.1 类静态成员:静态成员变量,静态成员函数
//#define LESSON11_2_class_static_count //11.2 类静态成员:静态成员变量,静态成员函数,统计创建对象个数
//#define LESSON11_3_class_this_point //11.3 this指针: this == &对象。对象其实就是结构体,对象指针就是结构体地址。通过->访问类成员(变量,函数)
//#define LESSON11_4_class_inner //11.4 编译器内部运行机制,用户角度,编译器角度
/**
第12课:操作符重载上
描述:
0. 概念:操作符重载是谓操作符提供不同的语义(不同的功能)。
1. c++标准库就是用c++编写的类库和函数的集合。(并不是c++语言的一部分)
1. 操作符重载:operator的本质就是函数重载。故遵循函数重载规则。
2. 友元friend:对外部函数开放访问类类成员权限。
**/
//#define LESSON12_example12_1_stdlib //12.1 c++标准库:<cstdio>前缀为c,没有.h
//#define LESSON12_example12_2_stdlib //12.2 c++标准库:<iosteam> cout(显示器对象) cin(键盘对象)使用
//#define LESSON12_example12_3_class_plus //12.3 类对象:相加编译异常
//#define LESSON12_example12_4_class_plus //12.4 类对象:相加使用函数方式
//#define LESSON12_example12_6_operator_overload //12.6 操作符重载:operator+,使用全局函数方式,故需要友元:friend
//#define LESSON12_example12_7_operator_overload //12.7 操作符重载:operator<<,使用全局函数方式,故需要友元:friend
/**
第13课:操作符重载下
描述:
1. =,[],(),->只能通过成员函数来重载,不可以通过全局函数来重载。C++限制的,因为确保为左值使用。
否则编译异常:std::ostream& operator->(std::ostream&, const Complex&)' must be a nonstatic member function
2. C++通过占位符来区分,前置++(需要占位符),还是后置++(不需要占位符)。至于为什么,以后理解
3. C++不要重载 &&,||操作符,违背了短路功能。
解释:为什么=,[],(),->等运算符只能重载成类的成员函数?
由上面的代码可以知道,如果将 =,[],(),-> 进行友元全局重载,那么就会出现 1=x; 1[x]; 1->x; 1(x);
这样的合法语句(起码编译器认为这些是合法的)--参考代码中的 if(1<x) 合法的片段,但显然这些是需要避免的,
当然这些不是我们程序员的责任,应该有语言的设计者来实现,所以,就……。
总结:1. 使用成员函数为了使对象为第一个参数。像[],(),->,=必须是对象为左值。
使用全局函数可能出现第一个参数不为对象而
**/
//#define LESSON13_example13_1_operator_overload_class_inner //13.1 操作符重载:使用类成员函数实现,故不需要友元:friend
//#define LESSON13_example13_2_operator_overload_class_array //13.2 操作符重载:只要是标准没有实现的,都可以通过重载来手动的实现 对象+,=,==,!=。
//#define LESSON13_example13_3_operator_overload_plusplus //13.3 操作符重载:operator++, //有占位符,后++ ,//无占位符,前++,至于为什么,以后理解
//#define LESSON13_example13_4_operator_overload_and_or //13.4 操作符重载:operator&&,operator||,不要重载,违背了短路功能。
//#define LESSON13_example13_5_operator_overload_operator_four //13.5 操作符重载:operator[],=,(),->,解释为什么只能通过成员函数方式来重载。确保为左值,C++限制的。
/**
第14课:专题二经典问题解析
描述:
1. 问题:malloc与free和new与delete有什么区别?
2. 编译器对构造函数得调用
3. 无状态函数,状态函数
4. 类可以实现状态函数,并且这个状态函数可以从头再来。
问题:malloc与free和new与delete有什么区别?
1. malloc free是库函数,new delete是关键字。
2. malloc以字节为单位申请堆内存。new以类型为单位申请对内存。
3. malloc delete只是单纯地对内存进行申请和释放。
4. new delete对基本类型申请空间时可以进行初始化。对类类型则会调用构造函数和析构函数。
**/
//#define LESSON14_example_14_1_new_mallloc //14.1 new malloc区别: 都是申请堆内存, new可以初始化,调用构造函数功能。
//#define LESSON14_example_14_2_constructor //14.2 编译器对构造函数调用:默认标准调用 Test t1(5);explicit告诉编译器不要自动调用构造函数
//#define LESSON14_example_14_3_class_member_static //14.3 静态成员变量作用: 用于控制对象数目。单一对象系统
//#define LESSON14_example_14_4_func_auto_static //14.4 函数中静态、自动变量:静态变量有记忆功能,无法从头再来。自动变量没有记忆功能,每次都是重新开始。
//#define LESSON14_example_14_5_class_auto_static //14.5 类可以实现状态函数,并且这个状态函数可以从头再来。
/**
第15课:惊艳的继承
0.继承概念:类之间的父子关系
0.1 子类拥有父类所有成员
0.2 子类就是一种特殊的父类,子类对象可以当做父类对象使用
0.3 子类可以拥有父类没有的方法和属性。
0.4 意义:代码复用
1. 类成员访问级别设定:
1.1 需要被外界访问的成员设置为:public
1.2 只能在当前类访问的成员设置为:private
1.3 只能在当前类和子类访问的成员设置为:protected
备注:private成员在子类依然存在,只是无法访问。
2.继承方式:
2.1 public继承:父类所有成员属性都被子类继承了,但是子类内部和外界都无法访问父类的私有成员
2.2 protected继承,父类public成员退化为子类的protected成员,可以在子类内部使用,不可以在外界使用。
2.3 private继承,父类public、protected成员退化为子类的private成员, 可以在子类内部使用,不可以在外界使用。
**/
//#define LESSON15_example15_1_jicheng_default //15.1 默认继承:父类所有成员都变为子类private成员
//#define LESSON15_example15_2_jicheng_public //15.2 public继承:父类所有成员属性都被子类继承了,但是无法访问父类的私有成员
//#define LESSON15_example15_3_jicheng_public //15.3 public继承:父类所有成员属性都被子类继承了,可以在子类直接访问父类的protected成员
//#define LESSON15_example15_4_jicheng_public_protected_private //15.4 继承:public、protected、private继承区别
/**
第16课:继承中的构造与析构
1. 继承构造函数调用顺序:先调用父类,再调用子类构造函数,析构函数相反
2. 子类继承父类:且包好其他类成员: 构造函数调用顺序:先父母,后客人,再自己。
3. 父类子类同名成员变量: 子类同时包含两个成员变量。存放在不同作用域
4. 父类对象可以包含子类对象,访问的是父类的方法。
子类成员变量名与父类成员变量名相同
1. 子类同时包含两个成员变量。存放在不同作用域
2. 通过不同作用域来访问两个变量:2.1 Child::成员变量,2.2 Parent::成员变量
3. 默认成员变量是子类成员变量
**/
//#define LESSON16_example16_1_parent_save_child //16.1 父类对象可以包含子类对象,访问的是父类的方法。
//#define LESSON16_example16_2_class_parent_child_constructor //16.2 继承构造函数调用顺序:先调用父类,再调用子类构造函数,析构函数相反
//#define LESSON16_example16_3_class_parent_child_constructor_param_list //16.3 继承构造函数:参数列表
//#define LESSON16_example16_4_class_parent_child_others //16.4 子类继承父类:且包好其他类成员: 构造函数调用顺序:先父母,后客人,再自己。
//#define LESSON16_example16_5_class_parent_same_member_name //16.5 父类子类同名成员变量: 子类同时包含两个成员变量。存放在不同作用域
/**
第17课:继承与多态上
1. 多态: 同样的调用语句有不同的表现形式(自动探测功能)。
1.1 根据实际对象而调用相应的方法。如:Parent *p; 若p指向父类对象则调用父类的方法,若p指向子类对象则调用子类重写的方法。
2. 多态通过关键字virtual来实现。唯一的方式。
2.1 被重写的虚函数即可表现出多态特性。
函数重写:
1. 概念:子类定义了与父类相同原型的函数。
2. 函数重写只发生在父类与子类之间
3. 父类被重写的函数依然存继承给子类。
4. 通过作用域标识符::可以访问父类被重写的函数。
**/
//#define LESSON17_example17_1_class_member_same_func_name //17.1 子父类相同成员函数名:子类重写父类成员函数,父类成员函数依然存在子类之中。
//#define LESSON17_example17_2_class_parent_save_child //17.2 父类接受子类:只能调用父类的方法。
//#define LESSON17_example17_3_class_parent_save_child_virtual //17.2 父类接受子类:多态:根据实际对象而调用相应的方法
/**
第18课:继承与多态下
0. 小结:
4.1 函数重载与函数重写不同。
4.2 多态是通过虚函数表实现的。
4.3 虚函数在效率会受到影响。
4.4 抽象类用于表示现实世界中的抽象概念:如形状。
4.5 抽象类是通过纯虚函数实现的。
1. 问题:重载和重写有什么区别?什么时候是重载,什么时候是重写?
1.1 重载发生在同一作用域里面(同一个类里面),故子类无法重载父类的函数。
1.2 重写发生在父类与子类之间的相同类型函数。
1.3 重载在编译阶段就根据参数类型及个数就确定了调用哪个函数。而重写是在运行阶段根据实际对象调用哪个函数。
2. 重写:多态:虚函数原理:
类中声明虚函数时,编译器会生成一个虚函数表,虚函数表就是存储成员函数指针的结构。
函数调用时,会判断是否为虚函数,若是虚函数则遍历虚函数表,并找到对象的函数指针,并执行。
若不是虚函数,则直接调用成员函数。(少了个遍历的过程,效率高些)
3. 是否可以将每个类成员函数都声明为虚函数?
回答:不可以,虚函数调用时会有额外的开销,若对运行效率要求较高的话,最好不要使用虚函数。
构造函数无法发生多态。
原因:对象在创建的时候又编译器对虚函数表指针进行初始化。
只有构造函数完成后虚函数表的指针才确定指向父类还是子类。
**/
//#define LESSON18_example18_1_class_overwrite_overload //18.1 重写重载区别:重载发生在同一作用域,重写发生在父类子类之间。
//#define LESSON18_example18_2_class_constructor_overwrite //18.2 构造函数无法调用重写函数,故无法发生多态。
//#define LESSON18_example18_2_class_pure_virtual_func //18.3 纯虚函数: 只有接口,没有实现,不能创建对象,可以用来定义指针接受子类对象。
/**
第19课:专题三经典问题解析
1. 函数重写:多态: 不要将多态应用于数组
2. cout格式化输出:十六进制hex,十进制dec,八进制oct
3. 多继承:
3.1 多继承带来代码复杂性、难以维护:单继承为树关系,多继承为图关系。 故既要考虑父类也要考虑母类。
3.2 多继承存在二义性,两个父类有相同的成员变量时,编译不通过。
3.3 可以用其他设计方法来代替多继承:单继承+接口,然后子类重写父类的方法,或者实现接口的方法。
4. 接口:
4.1 C++没有接口这个概念,当年还没有接口这个说法。
4.2 接口类只是功能说明,不是功能实现。
4.3 接口可以通过纯虚函数来是实现。
程序挂掉了
1. p++ 等同于p+1; 实现:(unsigned int)p + 1*sizeof(*p) , 以p类型为偏移单位。
2. 而重写:多态,是通过虚函数表指针来查找相应函数指针。
3. 当子类的大小比父类大小大时,偏移大小便不同了,故虚函数表指针需错位,故找不到相应函数指针。
**/
//#define LESSON19_example19_1_class_virtual_array //19.1 函数重写:多态:不要将多态应用于数组
//#define LESSON19_example19_2_class_virtual_array //19.2 函数重写:多态:不要将多态应用于数组
//#define LESSON19_example19_3_class_more_jicheng //19.3 多重继承:面向对象语言中只有C++支持,存在二义性,增加了程序复杂性,故被工程抛弃。
//#define LESSON19_example19_4_class_more_jicheng_interface //19.4 多重继承:可以通过单继承+接口来实现。
/**
第20课:泛型编程:函数模板
1. 函数模板:同一个接口接受不同类型数据。代码复用,解决代码冗余问题。
1.1 提供一种特殊的函数可用于不同类型进行调用。(表象)
1.2 编译器根据具体类型产生不同函数,帮你写代码(本质)
2. 函数模板:重载
2.1 函数模板可以被重载,跟普通函数一样。
2.2 编译器优先考虑匹配普通函数。
2.3 若函数模板可以产生更好的匹配,则选择函数模板。如:普通函数只有int func(int), 模板:T func(T,T),若调用func(1,2)则调用函数模板。
2.4 可以手动指定调用函数模板。通过func<>()指定
2.5 函数模板不允许自动类型转换,普通函数可以。
**/
//#define LESSON20_example_20_1_func_template //20.1 函数模板:根据实际类型编译器自动生成相应函数。(帮你做了重复的事情)
//#define LESSON20_example_20_2_func_template //20.2 函数模板:同一个接口接受不同类型数据。代码复用,解决代码冗余问题。
//#define LESSON20_example_20_3_func_template_overload //20.3 函数模板:重载:跟普通函数一样。本质还是根据语义产生不同函数名称
//#define LESSON20_example_20_4_func_template_more_param //20.4 函数模板:多数据类型 template<typename RT, typename T1, typename T2>
/**
第21课:泛型编程:类模板
1. 类模板的思想与函数模板相同。相同类自动适应不同类型操作。
2. 类定义之前加上 template<typename T>。操作对象是类成员。
3. 使用上只需要定义时指明类型。 如:Array<int> ai(5);
4. 工程应用:
4.1 将类模板定义到头文件中
4.2 或者定义一个.hpp文件存放类函数实现,.h文件存放声明,而.hpp包含.h文件。
**/
//#define LESSON21_example_21_1_class_template //21.1 类模板:类定义之前加上 template<typename T>。操作对象是类成员。
//#define LESSON21_example_21_2_class_template_array //21.2 类模板:泛型数组类,定义时指明类型即可,使用上没差别
//#define LESSON21_example_21_2_class_template_special //21.3 类模板:特化: 指明一个特殊的类调用 template<>
/**
* 第23课:STL: 标准模板库:常用数据结构和算法的集合
* 1. 容器:数组,栈,队列,链表
* 2. 迭代器:可以理解为元素指针
* 3. 算法: 排序,随机排列,遍历,查找,转换, 合并
*
* 意义:提高开发效率,及程序可靠性
* 书籍:1. C++ STL标准程序库开发指南, 2. Effective STL
**/
//#define LESSON23_example23_1_stl_verctor //23.1 容器:向量模板,可以用于数组
//#define LESSON23_example23_2_stl_stack_queue //23.2 容器:栈,队列
//#define LESSON23_example23_3_stl_list //23.3 容器:迭代器: 链表
//#define LESSON23_example23_1_stl_algorithm //23.4 算法
/**
第25课:异常处理上
1. 除0异常无法后果无法预知,强大系统可能只是程序挂掉,小系统可能系统挂掉。
2. 快速看代码经验:只看正常情况代码。
3. c++处理异常:try处理正常 catch处理异常 throw抛出异常,正常程序异常程序分开。
4. c++异常处理:一个try 可以对应多个catch,只能精确匹配。
5. 若抛出来的异常catch匹配不上,则往上抛,若上层也无法catch,则异常终止程序。
**/
//#define LESSON25_example25_1_exception_handle_c //25.1 c语言异常处理:若出现除0异常,通过&v指针返回状态值。
//#define LESSON25_example25_2_exception_handle_c //25.2 c语言异常处理:正常情况跟异常情况混在一起。显得冗余臃肿
//#define LESSON25_example25_3_exception_handle_c_goto //25.3 c语言异常处理:goto处理方式,正常异常处理分开。
//#define LESSON25_example25_4_exception_c_plusplus_try_catch //25.4 c++异常处理:try处理正常 catch处理异常 throw抛出异常,正常程序异常程序分开。
//#define LESSON25_example25_5_exception_c_plusplus_try_catch //25.5 c++异常处理:try处理正常 catch处理异常 throw抛出异常,返回值都可以不需要了
//#define LESSON25_example25_6_exception_c_plusplus_try_catch //25.5 c++异常处理:一个try 可以对应多个catch,只能精确匹配。
/**
第26课:异常处理下
1. catch(...)可以捕获所有异常
2. catch(...)经常作为最后一个catch出现。前面可以是特定的异常:如catch(char e)
3. 不要在构造函数里面抛出异常,将导致析构函数无法被调用,出现内存泄露
4. 工程应用:使用标准库的exception异常类,#include <stdexcept>
4.1. 软件异常:logic_error (参数异常)
4.2. 硬件异常:runtime_error (硬件故障,硬件错误,内存耗尽)
4.3 what()方法打印异常
5. 函数与try结合:函数体庞大时提高代码的可读写。
**/
//#define LESSON26_example26_1_exception //26.1 c++异常处理:catch(...)可以接收所有异常类型。
//#define LESSON26_example26_2_exception_catch_throw // 26.2 c++异常处理:catch中抛出异常。将被外面的catch捕获
//#define LESSON26_example26_3_exception_catch_throw // 26.3 c++异常处理:catch(...)中抛出异常。原封不动抛出。
//#define LESSON26_example26_4_exception_constructor // 26.4 c++异常处理:不要在构造函数里面抛出异常。资源泄露。
//#define LESSON26_example26_5_exception_project_used //26.5 工程应用:统一使用标准库的异常类exception
//#define LESSON26_example26_6_exception_fuc_try //26.6 函数与try结合:try里面就是正常程序,也就是函数体。
/**
第27课:类动态类型识别
1. 通过父类来接收子类对象,使用上动态识别出子类还是父类对象。
2. 方法一:typeid返回type_info类对象引用,typeid(对象名); typeid(类名)同一个意思。返回同一个type_info引用。#include <typeinfo>
3. 方法二:dynamic_cast :父类对象指针转换为子类对象指针,若返回空指针则表示不是该子类对象,若不为空指针,则表示为该子类对象
4. 方法三:唯一ID: 设计唯一ID属性,根据多态返回唯一的ID。若方法返回的ID与属性的ID一致,则表示同一类型。
实现功能:通过dynamic_cast,动态识别对象类型
优势:
1. 不用显示额外声明虚函数,用析构函数即可
2. 不用为每个类分配ID
缺陷:
1. 要求目标对象类型是多态的。也就是基类至少有一个虚函数,通声明析构函数为虚函数
**/
//#define LESSON27_example_27_1_class_type_identify_with_virtual //27.1 方法一:virtual虚函数 + 唯一ID
//#define LESSON27_example_27_2_class_type_identify_with_dynamic_cast //27.2 方法二:dynamic_cast + 父类为虚函数(可以虚析构函数)
//#define LESSON27_example_27_3_class_type_identify_with_typeid //27.3 方法三:typeid(类型) ,专门用于类型识别的,最好的方法
//27.3 方法三:typeid(类型) ,专门用于类型识别的,最好的方法
#ifdef LESSON27_example_27_3_class_type_identify_with_typeid
#include <cstdlib>
#include <iostream>
#include <typeinfo> //包含typeinfo头文件
using namespace std;
class Parent
{
public:
virtual ~Parent()
{
}
};
class Child : public Parent
{
public:
int add(int a, int b)
{
return a + b;
}
};
class NewChild : public Parent
{
};
void test(Parent* p)
{
//类型匹配
if( typeid(*p) == typeid(Child) )
{
//动态类型转换
Child* c = dynamic_cast<Child*>(p);
cout<<"Dynamic Type: "<<"Child"<<endl;
cout<<"add: "<<c->add(2, 3)<<endl;
}
else if( typeid(*p) == typeid(NewChild) )
{
cout<<"Dynamic Type: "<<"NewChild"<<endl;
}
else if( typeid(*p) == typeid(Parent) )
{
cout<<"Dynamic Type: "<<"Parent"<<endl;
}
}
/*
typeid专门用于类型识别。最好的方法
*/
int main(int argc, char *argv[])
{
Parent parent;
Child child;
NewChild nc;
int index;
char ch;
//typeid返回type_info类对象引用:该对象是基于类型来操作的。
const type_info& tp = typeid(parent); //typeid(Parent)同一个意思。
const type_info& tc = typeid(char);
const type_info& tn = typeid(nc);
const type_info& ti = typeid(index);
const type_info& tch = typeid(ch); //typeid(char) 同一个意思
cout<<tp.name()<<endl; //6Parent
cout<<tc.name()<<endl; //5Child
cout<<tn.name()<<endl; //8NewChild
cout<<ti.name()<<endl; //i
cout<<tch.name()<<endl; //c
test(&parent);
test(&child);
test(&nc);
const type_info& infop = typeid(parent); //typeid(Parent)同一个意思。返回同一个type_info引用
const type_info& infoP = typeid(Parent);
cout<<hex << &parent <<endl;//0x28fef0
cout<<hex << &infop <<endl; //0x476aec
cout<<hex << &infoP <<endl; //0x476aec
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//27.2 类动态类型识别:dynamic_cast + 父类为虚函数(可以虚析构函数)
#ifdef LESSON27_example_27_2_class_type_identify_with_dynamic_cast
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
//条件:必须有个虚函数
virtual ~Parent()
{
}
};
class Child : public Parent
{
public:
int add(int a, int b)
{
return a + b;
}
};
class NewChild : public Parent
{
};
void test(Parent* p)
{
//dynamic_cast<XXX*>(p),XXX需要用子类的类型指针:若类型匹配则返回非空指针(子类对象的地址),若不匹配则返回空指针
Child* c = dynamic_cast<Child*>(p);
if( c != NULL )
{
cout<<"Dynamic Type: "<<"Child"<<endl;
cout<<"add: "<<c->add(2, 3)<<endl;
}
else
{
if( dynamic_cast<NewChild*>(p) != NULL )
{
cout<<"Dynamic Type: "<<"NewChild"<<endl;
}
else
{
cout<<"Dynamic Type: "<<"Parent"<<endl;
}
}
}
/*
实现功能:通过dynamic_cast,动态识别对象类型
优势:
1. 不用显示额外声明虚函数,用析构函数即可
2. 不用为每个类分配ID
缺陷:
1. 要求目标对象类型是多态的。也就是基类至少有一个虚函数,通声明析构函数为虚函数
*/
int main(int argc, char *argv[])
{
Parent parent;
Child child;
NewChild nc;
test(&parent);
test(&child);
test(&nc);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//27.1 类动态类型识别:virtual虚函数 + 唯一ID
#ifdef LESSON27_example_27_1_class_type_identify_with_virtual
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
//有唯一的ID
enum { ID = 0 };
//根据多态返回唯一的ID。若方法返回的ID与属性的ID一致,则表示同一类型。
virtual int type()
{
return ID;
}
};
class Child : public Parent
{
public:
enum { ID = 1 };
int type()
{
return ID;
}
int add(int a, int b)
{
return a + b;
}
};
//使用父类指针进行接收类型,使用时需要识别具体类型。
void test(Parent* p)
{
//type是父类子类共有的,发生多态时会执行具体对象的方法。可以通过该方法返回唯一ID进行比较,达到动态识别效果
if( p->type() == Child::ID )
{
Child* c = (Child*)p;
cout<<"Dynamic Type: "<<"Child"<<endl;
cout<<"add: "<<c->add(2, 3)<<endl;
}
if( p->type() == Parent::ID )
{
cout<<"Dynamic Type: "<<"Parent"<<endl;
}
}
/*
实现功能:通过virtual虚函数,动态识别对象类型
条件:
1. 必须从基类开始就提供虚函数
2. 所有派生类必须重写虚函数
3. 每个派生类都必须有唯一的ID
缺陷:
1. 可以满足工程动态识别类类型需要,但是随着派生类增多,维护性会变差(主要是唯一ID的确定)
*/
int main(int argc, char *argv[])
{
Parent parent;
Child child;
test(&parent);
test(&child);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//26.6 函数与try结合:try里面就是正常程序,也就是函数体。
#ifdef LESSON26_example26_6_exception_fuc_try
#include <cstdlib>
#include <iostream>
#include <stdexcept>
using namespace std;
int func1(int i)
{
try
{
if( i > 0 )
{
return i;
}
else
{
throw "error";
}
}
catch(...)
{
return -1;
}
}
//函数与try结合
int func2(int i) try
{
//try里面就是正常程序,也就是函数体。
if( i > 0 )
{
return i;
}
else
{
throw "error";
}
}
catch(...)
{
return -1;
}
int main(int argc, char *argv[])
{
for(int i=0; i<5; i++)
{
cout<<func2(i)<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//26.5 工程应用:统一使用标准库的异常类exception
#ifdef LESSON26_example26_5_exception_project_used
#include <cstdlib>
#include <iostream>
#include <stdexcept> //标准库的异常类头文件。
using namespace std;
//通过继承的方式,命名本地化了。
class divide_by_zero : public logic_error
{
public:
//字节将构造函数参数传递个父类
divide_by_zero(const char* s) : logic_error(s)
{
}
};
double Div(double a, double b)
{
if( (-0.00000001 < b) && ( b < 0.00000001) )
{
//抛出异常对象
//throw logic_error("Divide by zero...");
throw divide_by_zero("Divide by zero..."); //更加直观
}
return a / b;
}
/*
//工程应用:统一使用一个异常类。
标准库异常类:exception类有两个分支
1. 软件异常:logic_error (参数异常)
2. 硬件异常:runtime_error (硬件故障,硬件错误,内存耗尽)
what()方法打印异常
*/
int main(int argc, char *argv[])
{
try
{
cout<<Div(1, 0)<<endl;
}
//值拷贝会有额外的开销,比如创建对象,故使用引用。 可以用父类来接收。
catch(exception& e)
{
//对象what方法类获取具体异常消息
cout<<e.what()<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
// 26.4 c++异常处理:不要在构造函数里面抛出异常。资源泄露。
#ifdef LESSON26_example26_4_exception_constructor
#include <cstdlib>
#include <iostream>
using namespace std;
class Test
{
int* p;
public:
Test()
{
cout<<"Test()"<<endl;
p = new int[5];
//不要在构造函数里面抛出异常:导致对象构造不完全,对象的析构函数将无法被调用 ,导致资源泄露。
//throw 10;
}
~Test()
{
cout<<"~Test()"<<endl;
delete[] p;
}
};
int main(int argc, char *argv[])
{
try
{
Test t;
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
// 26.3 c++异常处理:catch(...)中抛出异常。原封不动抛出。
#ifdef LESSON26_example26_3_exception_catch_throw
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( (6 <= i) && (i <= 9) )
{
throw i;
}
return i;
}
int main(int argc, char *argv[])
{
try
{
for(int i=0; i<10; i++)
{
try
{
cout<<test(i)<<endl;
}
catch(...)
{
cout<<"Exception Occur"<<endl;
//可变参捕获的异常原封不动抛出。
throw;
}
}
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
// 26.2 c++异常处理:catch中抛出异常。将被外面的catch捕获
#ifdef LESSON26_example26_2_exception_catch_throw
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( (6 <= i) && (i <= 9) )
{
throw i;
}
return i;
}
int main(int argc, char *argv[])
{
try
{
for(int i=0; i<10; i++)
{
try
{
cout<<test(i)<<endl;
}
catch(int e)
{
cout<<"Exception: "<<e<<endl;
//catch中抛出异常。将被外面的catch捕获
throw e;
}
}
}
catch(int e)
{
cout<<"Catch: "<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//26.1 c++异常处理:catch(...)可以接收所有异常类型。
#ifdef LESSON26_example26_1_exception
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( i == 1 )
{
throw "p";
}
if( i == 2 )
{
throw 0.5;
}
if( i == 3 )
{
throw 3;
}
if( i == 4 )
{
throw 'c';
}
return i;
}
int main(int argc, char *argv[])
{
for(int i=0; i<10; i++)
{
/*
该设计方法类似用c语言实现:
1. return 同一类型的各个返回值。
2. 上层函数(存储异常后)判断是否为异常情况(catch(...)),若为异常情况则将该异常返回给上层(throw)。
缺陷:无法抛出不同类型异常。
*/
try
{
cout<<test(i)<<endl;
}
//多个catch捕获异常是,特殊的catch排列在前面
catch(char e)
{
cout<<"Exception: "<<e<<endl;
}
//可变参: 接收全部异常。
catch(...)
{
cout<<"Exception Occur"<<endl;
}
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//25.5 c++异常处理:一个try 可以对应多个catch,只能精确匹配。
#ifdef LESSON25_example25_6_exception_c_plusplus_try_catch
#include <cstdlib>
#include <iostream>
using namespace std;
int test(int i)
{
if( i == 1 )
{
//异常值为:int整型
throw -1;
}
if( i == 2 )
{
//异常值为:const char* 字符串
throw "ERROR"; //字符串类型为:const char*
}
if( i == 3 )
{
//异常值为:double浮点型
throw 0.5;
}
if( i == 4 )
{
//异常值为:char字符型
throw 'd';
}
return i;
}
/*
catch异常
1. 一个try 可以对应多个catch
2. 不能模糊匹配,不能强制类型转化,只能精确匹配。
*/
int main(int argc, char *argv[])
{
for(int i=0; i<5; i++)
{
/*
该设计方法类似用c语言实现:
1. return 同一类型的各个返回值。
2. 上层函数判断一一判断返回值(冗余),if() else if () else if ()..
*/
try
{
cout<<test(i)<<endl;
}
//捕获异常值为:整型
catch(int e)
{
cout<<"Int: "<<e<<endl;
}
//捕获异常值为:字符串
catch(const char* e)
{
cout<<"const char*: "<<e<<endl;
}
//捕获异常值为: 浮点型
catch(double e)
{
cout<<"double: "<<e<<endl;
}
//捕获异常值为: 字符型
catch (char e)
{
cout << "char:" <<e <<endl;
}
//批注:若抛出来的异常catch匹配不上,则往上抛,若上层也无法catch,则异常终止程序。
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//25.5 c++异常处理:try处理正常 catch处理异常 throw抛出异常,返回值都可以不需要了
#ifdef LESSON25_example25_5_exception_c_plusplus_try_catch
#include <cstdlib>
#include <iostream>
using namespace std;
void MemSet(void* dest, unsigned int length, unsigned char v)
{
if( dest == NULL )
{
//抛出异常
throw -1;
}
if( length < 4 )
{
//抛出异常
throw -2;
}
if( (v < 0) || (v > 9) )
{
//抛出异常
throw -3;
}
//通过异常检测,返回值都可以不需要了
unsigned char* p = (unsigned char*)dest;
for(int i=0; i<length; i++)
{
p[i] = v;
}
}
int main(int argc, char *argv[])
{
int ai[5];
double ad[4];
char ac[3];
//正常处理程序
try
{
MemSet(ai, sizeof(ai), 0);
MemSet(ad, sizeof(ad), 1);
MemSet(ac, sizeof(ac), 2);
}
//异常处理程序:参数为throw处理的异常值
catch(int e)
{
cout<<e<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//25.4 c++异常处理:try处理正常 catch处理异常 throw抛出异常,正常程序异常程序分开。
#ifdef LESSON25_example25_4_exception_c_plusplus_try_catch
#include <cstdlib>
#include <iostream>
using namespace std;
#define DIV_ZERO_ERROR -1
double Div(double a, double b)
{
if( (-0.000000001 < b) && (b < 0.000000001) )
{
//抛出异常。
throw DIV_ZERO_ERROR;
}
return a / b;
}
/*
快速看代码经验:只看正常情况代码。
*/
int main(int argc, char *argv[])
{
//正常程序
try
{
cout<<Div(3, 1.1)<<endl;
//除0异常发生
cout<<Div(1, 0)<<endl;
cout<<"continue: "<<endl;
cout<<Div(1, 2)<<endl;
}
//异常程序:捕获throw出来的异常。
//若异常没有catch处理,则返回上层,若上层没有catch处理。则异常终止程序。
catch(int error)
{
cout<<"Exception: "<<endl;
cout<<error<<endl;
}
//test
try
{
cout << "try start:" << endl;
throw 1;
cout << "try end:" << endl;
}
catch(int error)
{
cout << "catch:" << endl;
cout << "error:" << error << endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//25.3 c语言异常处理: goto处理方式,正常异常处理分开。
#ifdef LESSON25_example25_3_exception_handle_c_goto
#include <cstdlib>
#include <iostream>
using namespace std;
int MemSet(void* dest, unsigned int length, unsigned char v)
{
if( dest == NULL )
{
return -1;
}
if( length < 4 )
{
return -2;
}
if( (v < 0) || (v > 9) )
{
return -3;
}
unsigned char* p = (unsigned char*)dest;
for(int i=0; i<length; i++)
{
p[i] = v;
}
return 0;
}
int main(int argc, char *argv[])
{
int ai[5];
double ad[4];
char ac[3];
int ret = 0;
ret = MemSet(ai, sizeof(ai), 0);
if( ret != 0 )
{
goto ERROR;
}
//使用goto来处理异常程序。 //goto很危险,可能会跳过重要的初始化程序。
ERROR:
//统一释放资源等
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//25.2 c语言异常处理: 正常情况跟异常情况混在一起。显得冗余臃肿
#ifdef LESSON25_example25_2_exception_handle_c
#include <cstdlib>
#include <iostream>
using namespace std;
int MemSet(void* dest, unsigned int length, unsigned char v)
{
//异常判断。并且返回各种异常值
if( dest == NULL )
{
return -1;
}
if( length < 4 )
{
return -2;
}
if( (v < 0) || (v > 9) )
{
return -3;
}
unsigned char* p = (unsigned char*)dest;
for(int i=0; i<length; i++)
{
p[i] = v;
}
return 0;
}
int main(int argc, char *argv[])
{
int ai[5];
double ad[4];
char ac[3];
int ret;
ret = MemSet(ai, sizeof(ai), 0);
//正常状态,跟异常状态混在一起,不好区分
//优化:1. 可以通过goto来分开正常异常代码,但是goto很危险喔。2. 可以通过宏来区别。
if( ret == 0 )
{
}
else if( ret == -1 )
{
}
else if( ret == -2 )
{
}
else if( ret == -3 )
{
}
ret = MemSet(ad, sizeof(ad), 1);
//该异常处理方式显得冗余,臃肿。增加阅读难度。
//优化:可以通过异常出现则往上抛,由上层处理异常情况。 但是这样每次都得释放资源处理。代码膨胀。
if( ret == 0 )
{
}
else if( ret == -1 )
{
}
else if( ret == -2 )
{
}
else if( ret == -3 )
{
}
ret = MemSet(ac, sizeof(ac), 2);
if( ret == 0 )
{
}
else if( ret == -1 )
{
}
else if( ret == -2 )
{
}
else if( ret == -3 )
{
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//25.1 c语言异常处理: 若出现除0异常,通过&v指针返回状态值。
#ifdef LESSON25_example25_1_exception_handle_c
#include <cstdlib>
#include <iostream>
using namespace std;
//除法运算除0异常处理
double Div(double a, double b, bool* valid)
{
//没有指针异常判断
*valid = true;
//浮点数值判断
if( (-0.000000001 < b) && (b < 0.000000001) )
{
*valid = false;
return 0;
}
return a / b;
}
double Add(double a, double b)
{
return a + b;
}
double Minus(double a, double b)
{
return a - b;
}
double Multi(double a, double b)
{
return a * b;
}
int main(int argc, char *argv[])
{
bool v = false;
cout << "nomal case:" << endl;
cout<<Div(6, 2, &v)<<endl;
cout<<v<<endl;
/* c语言处理异常方式:
若出现除0异常,通过&v指针返回状态值。
问题是:倘若&指针也有异常呢,那么就引入另外一个异常。而解决另外一个异常则原来异常则解决不了。
*/
cout << endl << "error case:" << endl;
cout<<Div(Add(3, 3), Minus(2, 2), &v)<<endl;
cout<<v<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//23.4 算法
#ifdef LESSON23_example23_1_stl_algorithm
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void current(int& v)
{
cout<<v<<endl;
}
void print(vector<int>& vec)
{
cout<<"Elements in vector:"<<endl;
//for (int i=0; i<vec.size(); i++)
//{
// cout << vec[i] << endl;
//}
//遍历后回调current函数指针
for_each(vec.begin(), vec.end(), current);
}
int compare(const int& a, const int& b)
{
return a < b;
}
/*
* algorithm: 1.for_ench, 2. sort, 3. ramdom_shuffle
*/
int main(int argc, char *argv[])
{
vector<int> v(10);
for(int i=9; i>=0; i--)
{
v[i] = 9 - i;
}
print(v);
//使用默认的比较器<进行排序
//sort(v.begin(), v.end());
//根据函数返回值进行排序
sort(v.begin(), v.end(), compare);
//随机排列
//random_shuffle(v.begin(), v.end());
print(v);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//23.3 容器:迭代器: 链表
#ifdef LESSON23_example23_3_stl_list
#include <cstdlib>
#include <iostream>
#include <list> //链表头文件
using namespace std;
/*
* list: 1. push_back, 2.insert, 3.begin, 4. end
*/
int main(int argc, char *argv[])
{
list<double> l;
cout<<"l size: "<<l.size()<<endl;
for(int i=0; i<5; i++)
{
l.push_back((i + 1) / 1000.0); //最后位置插入
}
cout<<"l size: "<<l.size()<<endl;
//迭代器可以理解为元素指针
list<double>::iterator current = l.begin();
cout<<"Elements in l:"<<endl;
while( current != l.end() )
{
cout<<*current<<endl;
current++;
}
current = l.begin();
current++;
current++;
l.insert(current, 0.1); //当前位置插入
cout<<"Elements in l:"<<endl;
for(list<double>::iterator p = l.begin(); p != l.end(); p++)
{
cout<<*p<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//23.2 容器:栈,队列
#ifdef LESSON23_example23_2_stl_stack_queue
#include <cstdlib>
#include <iostream>
#include <stack>
#include <queue>
using namespace std;
/*
* stack: 1. push, 2.pop, 3.empty, 4.top
*/
void StackUsage()
{
cout<<"Stack Usage"<<endl;
stack<double> s; //定义栈对象
for(int i=0; i<5; i++)
{
s.push(i/100.0); //压栈操作
}
cout<<"Elements in s:"<<endl;
while( !s.empty() ) //判断栈是否为空
{
double v = s.top(); //获取栈顶元素操作
s.pop(); //出栈操作
cout<<v<<endl;
}
}
/*
* queue: 1. push, 2.pop, 3.empty, 4.front
*/
void QueueUsage()
{
cout<<"Queue Usage"<<endl;
queue<int> q;
for(int i=0; i<5; i++)
{
q.push(i + 1);
}
cout<<"Elements in q:"<<endl;
while( !q.empty() )
{
int v = q.front();
q.pop();
cout<<v<<endl;
}
}
int main(int argc, char *argv[])
{
StackUsage();
QueueUsage();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//23.1 容器:向量模板,可以用于数组
#ifdef LESSON23_example23_1_stl_verctor
#include <cstdlib>
#include <iostream>
#include <vector> //向量, 库数组类
using namespace std;
/*
* vector: 1. size, 2.resize, 3. begin, 4.end, 5.erase, 6.push_back, 7.insert, 8.swap
* 操作:创建对象,赋值,取值。
*/
int main(int argc, char *argv[])
{
vector<int> vi(20); //新建数组对象 _大小喔
cout<<"vi size: "<<vi.size()<<endl;
for(int i=0; i<5; i++)
{
vi[i] = i + 1;
}
vi.resize(5); //重新指定大小
cout<<"Elements in vi:"<<endl;
for(int i=0; i<vi.size(); i++)
{
cout<<vi[i]<<endl;
}
vector<int> vin;
vin = vi; //直接赋值 拷贝
vi.resize(0);
cout<<"Elements in vin:"<<endl;
for(int i=0; i<vin.size(); i++)
{
cout<<vin[i]<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//21.3 类模板:特化: 指明一个特殊的类调用 template<>
#ifdef LESSON21_example_21_2_class_template_special
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
class Test
{
public:
T test(T v)
{
cout<<"T test(T v)"<<endl;
cout<<"sizeof(T) = "<<sizeof(T)<<endl;
return v;
}
};
//类模板特化,指明特殊类型,然后将调用这里。
template<> //特殊指明
class Test<int> //特殊指明
{
public:
int test(int v)
{
cout<<"int test(int v)"<<endl;
cout<<"sizeof(int) = "<<sizeof(int)<<endl;
return v;
}
};
//继承特化类,之后使用就先普通类一样。
class MyTest : public Test<int>
{
public:
void print(void)
{
cout<<"MyTest void print(void)"<<endl;
}
};
int main(int argc, char *argv[])
{
Test<int> t1; //调用特化类:有点像函数模板里面的普通类型函数
Test<double> t2;
cout<<t1.test(1)<<endl;
cout<<t2.test(1.0)<<endl;
cout << endl << "MyTest" << endl;
MyTest m;
m.test(1);
m.print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//21.2 类模板:泛型数组类,定义时指明类型即可,使用上没差别
#ifdef LESSON21_example_21_2_class_template_array
//array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_
template<typename T>
class Array
{
private:
int mLength;
T* mSpace;
public:
Array(int length);
Array(const Array& obj);
int length();
~Array();
T& operator[](int i);
Array& operator= (const Array& obj);
bool operator== (const Array& obj);
bool operator!= (const Array& obj);
};
#endif
//array.hpp
#ifndef _ARRAY_DEF_H_
#define _ARRAY_DEF_H_
//#include "Array.h"
template<typename T>
Array<T>::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new T[mLength];
}
template<typename T>
Array<T>::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new T[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
template<typename T>
int Array<T>::length()
{
return mLength;
}
template<typename T>
Array<T>::~Array()
{
mLength = -1;
delete[] mSpace;
}
template<typename T>
T& Array<T>::operator[](int i)
{
return mSpace[i];
}
template<typename T>
Array<T>& Array<T>::operator= (const Array<T>& obj)
{
delete[] mSpace;
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
return *this;
}
template<typename T>
bool Array<T>::operator== (const Array<T>& obj)
{
bool ret = true;
if( mLength == obj.mLength )
{
for(int i=0; i<mLength; i++)
{
if( mSpace[i] != obj.mSpace[i] )
{
ret = false;
break;
}
}
}
else
{
ret = false;
}
return ret;
}
template<typename T>
bool Array<T>::operator!= (const Array& obj)
{
return !(*this == obj);
}
#endif
#include <cstdlib>
#include <iostream>
//#include "Array.hpp"
using namespace std;
/*
类模板工程应用:
1.
*/
int main(int argc, char *argv[])
{
//使用上只需要定义时指明类型。
Array<int> ai(5);
for(int i=0; i<ai.length(); i++)
{
ai[i] = i + 1;
}
for(int i=0; i<ai.length(); i++)
{
cout<<ai[i]<<endl;
}
Array<double> ad(10);
for(int i=0; i<ad.length(); i++)
{
ad[i] = (i + 1) / 100.0;
}
for(int i=0; i<ad.length(); i++)
{
cout<<ad[i]<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//21.1 类模板:类定义之前加上 template<typename T>。操作对象是类成员。
#ifdef LESSON21_example_21_1_class_template
#include <cstdlib>
#include <iostream>
using namespace std;
//声明泛型:类模板,与函数模板声明一样,操作对象是类成员。
template<typename T>
class Operator
{
public:
T add(T a, T b);
T minus(T a, T b);
};
//每个实现都得加上 泛型声明
template<typename T>
//类名称后面加上<typename>说明为泛型。
T Operator<T>::add(T a, T b)
{
return a + b;
}
template<typename T>
T Operator<T>::minus(T a, T b)
{
return a - b;
}
int main(int argc, char *argv[])
{
//类模板定义对象。
Operator<int> op1;
Operator<double> op2;
cout<<op1.add(5, 4)<<endl;
cout<<op1.minus(4, 5)<<endl;
cout<<op2.add(1.3, 0.01)<<endl;
cout<<op2.minus(0.01, 1.3)<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//20.4 函数模板:多数据类型 template<typename RT, typename T1, typename T2>
#ifdef LESSON20_example_20_4_func_template_more_param
#include <cstdlib>
#include <iostream>
using namespace std;
//声明以下可能会用到的数据类型
template<typename RT, typename T1, typename T2>
RT Add(T1 a, T2 b)
{
//强制类型转换
return static_cast<RT>(a + b);
}
int main(int argc, char *argv[])
{
//传统方式指定,用这种方式就好了。
cout<<Add<double, char, float>('a', 100.0f)<<endl;
//偷懒方式,将不是形参传递的类型放在首位。
cout<<Add<double>('a', 100.0f)<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//20.3 函数模板:重载: 跟普通函数一样。本质还是根据语义产生不同函数名称
#ifdef LESSON20_example_20_3_func_template_overload
#include <cstdlib>
#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 Max(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);
}
/*
函数模板:重载
1. 函数模板可以被重载,跟普通函数一样。
2. 编译器优先考虑匹配普通函数。
3. 若函数模板可以产生更好的匹配,则选择函数模板。如:普通函数只有int func(int), 模板:T func(T,T),若调用func(1,2)则调用函数模板。
4. 可以手动指定调用函数模板。通过func<>()指定
5. 函数模板不允许自动类型转换,普通函数可以。
*/
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl;
//手动指定函数模板方式: Max<>()
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; //cout<<"int Max(int a, int b)"<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//20.2 泛型编程:函数模板:同一个接口接受不同类型数据。代码复用,解决代码冗余问题。
#ifdef LESSON20_example_20_2_func_template
#include <cstdlib>
#include <iostream>
using namespace std;
//函数模板:交换两个值
template<typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
//函数模板:对数组进行排序
template<typename T>
void SelectSort(T array[], int length)
{
for(int i=0; i<length; i++)
{
T min = array[i];
int index = i;
for(int j=i+1; j<length; j++)
{
if( array[j] < min )
{
min = array[j];
index = j;
}
}
Swap<T>(array[i], array[index]);
}
}
/*
把template<typename T>的T当作一种类型,在函数使用的时候将这种类型具体化(指定)。
*/
int main(int argc, char *argv[])
{
int array[] = {3, 2, 5, 3 , 4};
//指明类型:函数名<type>()
SelectSort<int>(array, 5);
for(int i=0; i<5; i++)
{
cout<<array[i]<<endl;
}
cout << endl;
char ca[] = {'b', 'c', 'a', 'e', 'd', 'f'};
SelectSort(ca, 6);
for(int i=0; i<6; i++)
{
cout<<ca[i]<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//20.1 泛型编程:函数模板:同一个接口接受不同类型数据。代码复用,解决代码冗余问题。
#ifdef LESSON20_example_20_1_func_template
#include <cstdlib>
#include <iostream>
using namespace std;
//泛型编程:声明函数模板:函数声明定义前加上:template<typename T>
template<typename T>
//若T为int:则__Z4SwapIiEvRT_S1_:
void Swap(T& a, T& b)
{
cout << "void Swap(T& a, T& b)" <<endl;
T t = a;
a = b;
b = t;
}
//__Z4SwapRiS_: 与泛型可以同时存在因为是不同函数名
void Swap(int& a, int& b)
{
cout << "void Swap(int& a, int& b)" <<endl;
int t = a;
a = b;
b = t;
}
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
//默认:编译器自动推导类型。优先选择普通函数:void Swap(int& a, int& b)
//__Z4SwapRiS_:
Swap(a, b);
//若T为int:则__Z4SwapIiEvRT_S1_:
Swap<int>(a, b);
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
float fa = 3;
float fb = 4;
//手动指定类型。 在函数名称与括号之间加上<type>显式
//__Z4SwapIfEvRT_S1_:
Swap<float>(fa, fb);
cout<<"fa = "<<fa<<endl;
cout<<"fb = "<<fb<<endl;
char ca = 'a';
char cb = 'b';
//Swap(ca, 4); //Error 需要一一匹配。
//默认:编译器自动推导类型。
//__Z4SwapIcEvRT_S1_:
Swap(ca, cb);
cout<<"ca = "<<ca<<endl;
cout<<"cb = "<<cb<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//19.4 多重继承:可以通过单继承+接口来实现。
#ifdef LESSON19_example19_4_class_more_jicheng_interface
#include <cstdlib>
#include <iostream>
using namespace std;
//定义接口
class Interface1
{
public:
//使用纯虚函数来实现
virtual void print() = 0;
virtual int add(int i, int j) = 0;
};
struct Interface2
{
virtual int add(int i, int j) = 0;
virtual int minus(int i, int j) = 0;
};
//多继承:接口
class Child : public Interface1, public Interface2
{
public:
void print()
{
cout<<"Child::print"<<endl;
}
int add(int i, int j)
{
return i + j;
}
int minus(int i, int j)
{
return i - j;
}
};
int main(int argc, char *argv[])
{
Child c;
c.print();
cout<<c.add(3, 5)<<endl;
cout<<c.minus(4, 6)<<endl;
Interface1* i1 = &c;
Interface2* i2 = &c;
cout<<i1->add(7, 8)<<endl;
cout<<i2->add(7, 8)<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//19.3 多重继承:面向对象语言中只有C++支持,增加了程序复杂性,故被工程抛弃。
#ifdef LESSON19_example19_3_class_more_jicheng
#include <cstdlib>
#include <iostream>
using namespace std;
class Object
{
protected:
int d;
public:
void f()
{
cout<<"Object::f"<<endl;
}
};
//二义性可以通过虚继承来解决,但是问题来了,大工程怎么知道哪些要要虚
//class P1 : virtual public Object
class P1 : public Object
{
protected:
int i;
};
//class P2 : virtual public Object
class P2 : public Object
{
protected:
int j;
};
//多重继承
class Child : public P1, public P2
{
public:
Child(int i, int j)
{
//提示d是模糊的,编译器不知道怎么办了
this->d = 0;
this->i = i;
this->j = j;
}
void print()
{
cout<<"i = "<<i<<" "<<"j = "<<j<<endl;
}
};
int main(int argc, char *argv[])
{
Child c(1, 2);
c.print();
c.f();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//19.2 函数重写:多态: 不要将多态应用于数组
#ifdef LESSON19_example19_2_class_virtual_array
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
int i;
public:
virtual void f()
{
cout<<"Parent::f"<<endl;
}
};
class Child : public Parent
{
protected:
int j;
public:
Child(int i, int j)
{
this->i = i;
this->j = j;
}
void f()
{
cout<<"i = "<<i<<" "<<"j = "<<j<<endl;
}
};
int main(int argc, char *argv[])
{
Parent* p = NULL;
Child* c = NULL;
Child ca[3] = {Child(1, 2), Child(3, 4), Child(5, 6)};
p = ca;
c = ca;
cout<<hex<<p+1<<endl;
p->f();
c->f();
/*
程序挂掉了
1. p++ 等同于p+1; 实现:(unsigned int)p + 1*sizeof(*p) , 以p类型为偏移单位。
2. 而重写:多态,是通过虚函数表指针来查找相应函数指针。
3. 当子类的大小比父类大小大时,偏移大小便不同了,故虚函数表指针需错位,故找不到相应函数指针。
*/
cout << dec << endl;
//两个类大小不同
cout << "sizeof(Parent): " << sizeof(Parent) <<endl;
cout << "sizeof(Child): " << sizeof(Child) <<endl;
cout << hex << endl;
cout << "&ca[0]:" << &ca[0] << endl;//0x28fef4
cout << "&ca[1]:" << &ca[1] << endl;//0x28ff00 = 0x28fef4+0x0c = &ca[0] + sizeof(Parent)
cout<<"p++:"<<p+1<<endl;//0x28fefc = 0x28fef4+0x08 = &ca[0] + sizeof(Child) 故:不要将多态应用于数组
//p++; //Error
c++;
p->f();
c->f();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//19.1 函数重写:多态: 不要将多态应用于数组
#ifdef LESSON19_example19_1_class_virtual_array
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
int i;
public:
virtual void f()
{
cout<<"Parent::f"<<endl;
}
};
class Child : public Parent
{
protected:
int j;
public:
Child(int i, int j)
{
this->i = i;
this->j = j;
}
void f()
{
cout<<"i = "<<i<<" "<<"j = "<<j<<endl;
}
};
int main(int argc, char *argv[])
{
Parent* p = NULL;
Child* c = NULL;
Child ca[3] = {Child(1, 2), Child(3, 4), Child(5, 6)};
p = ca;
c = ca;
//iomanip.h是I/O流控制头文件,就像C里面的格式化输出一样.以下是一些常的:
//dec 置基数为10 相当于"%d"
//hex 置基数为16 相当于"%X"
//oct 置基数为8 相当于"%o"
cout<<dec<<16<<endl;
cout<<hex<<16<<endl;
cout<<oct<<16<<endl;
cout<<hex<<p+1<<endl;
p->f();
c->f();
/*
程序挂掉了
1. p++ 等同于p+1; 实现:(unsigned int)p + 1*sizeof(*p) , 以p类型为偏移单位。
2. 而重写:多态,是通过虚函数表指针来查找相应函数指针。
3. 当子类的大小比父类大小大时,偏移大小便不同了,故虚函数表指针需错位,故找不到相应函数指针。
*/
//p++; //Error
c++;
p->f();
c->f();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//18.3 纯虚函数: 只有接口,没有实现,不能创建对象,可以用来定义指针接受子类对象。
#ifdef LESSON18_example18_2_class_pure_virtual_func
#include <cstdlib>
#include <iostream>
using namespace std;
//形状是没有大小的。
class Shape
{
public:
//定义纯虚函数,只有接口,没有实现
virtual double area() = 0;
};
class Rectangle : public Shape
{
double m_a;
double m_b;
public:
Rectangle(double a, double b)
{
m_a = a;
m_b = b;
}
double area()
{
return m_a * m_b;
}
};
class Circle : public Shape
{
double m_r;
public:
Circle(double r)
{
m_r = r;
}
double area()
{
return 3.14 * m_r * m_r;
}
};
//纯虚函数可以用来接受它的子类对象。
void area(Shape* s)
{
cout<<s->area()<<endl;
}
int main(int argc, char *argv[])
{
Rectangle rect(2, 3);
Circle circle(4);
//纯虚函数无法创建对象
//Shape s;
area(&rect);
area(&circle);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//18.2 构造函数无法调用重写函数,故无法发生多态。
#ifdef LESSON18_example18_2_class_constructor_overwrite
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
Parent()
{
//创建子类对象时会调用父类构造函数,此时调用重写的函数,多态是不会发生的。
this->func(); //void Parent::func()
}
void rewrite(void)
{
this->func(); //void Child::func()
}
virtual void func()
{
cout<<"void Parent::func()"<<endl;
}
};
class Child : public Parent
{
public:
Child()
{
this->func();
}
void func()
{
cout<<"void Child::func()"<<endl;
}
};
/*
构造函数无法发生多态。
原因:对象在创建的时候又编译器对虚函数表指针进行初始化。
只有构造函数完成后虚函数表的指针才确定指向父类还是子类。
*/
int main(int argc, char *argv[])
{
Child c;
//会调用重写函数,发生多态。
c.rewrite();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//18.1 重写重载区别:重载发生在同一作用域,重写发生在父类子类之间。
#ifdef LESSON18_example18_1_class_overwrite_overload
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
char i;
public:
//同类(同一作用域)里面的同名函数发生重载
virtual void func()
{
cout<<"void func()"<<endl;
}
virtual void func(int i)
{
cout<<"void func(int i)"<<endl;
}
virtual void func(int i, int j)
{
cout<<"void func(int i, int j)"<<endl;
}
};
class Child : public Parent
{
public:
//重写父类成员函数
void func(int i, int j)
{
cout<<"void func(int i, int j)"<<" "<<i + j<<endl;
}
void func(int i, int j, int k)
{
cout<<"void func(int i, int j, int k)"<<" "<<i + j + k<<endl;
}
};
void run(Parent* p)
{
p->func(1, 2);
}
/*
1. 问题:重载和重写有什么区别?什么时候是重载,什么时候是重写?
1.1 重载发生在同一作用域里面(同一个类里面),故子类无法重载父类的函数。
1.2 重写发生在父类与子类之间的相同类型函数。
1.3 重载在编译阶段就根据参数类型及个数就确定了调用哪个函数。而重写是在运行阶段根据实际对象调用哪个函数。
2. 多态:虚函数原理:
类中声明虚函数时,编译器会生成一个虚函数表,虚函数表就是存储成员函数指针的结构。
函数调用时,会判断是否为虚函数,若是虚函数则遍历虚函数表,并找到对象的函数指针,并执行。
若不是虚函数,则直接调用成员函数。(少了个遍历的过程,效率高些)
3. 是否可以将每个类成员函数都声明为虚函数?
回答:不可以,虚函数调用时会有额外的开销,若对运行效率要求较高的话,最好不要使用虚函数。
*/
int main(int argc, char *argv[])
{
Parent p;
p.func();
p.func(1);
p.func(1, 2);
Child c;
c.func(1, 2);
run(&p);
run(&c);
cout << "sizeof(Parent) :" << sizeof(Parent) <<endl; //8: 4个字节是int i,4个字节是虚函数表指针
cout << "sizeof(Child) :" << sizeof(Child) <<endl; //8
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//17.2 父类接受子类:多态:根据实际对象而调用相应的方法
#ifdef LESSON17_example17_3_class_parent_save_child_virtual
#include <cstdlib>
#include <iostream>
using namespace std;
class Boss
{
private:
static Boss* cInstance;
Boss()
{
}
public:
static Boss* GetInstance()
{
if( cInstance == NULL )
{
cInstance = new Boss();
}
return cInstance;
}
int fight()
{
cout<<"Boss::fight()"<<endl;
return 10;
}
};
Boss* Boss::cInstance = NULL;
class Master
{
public:
virtual int eightSwordKill()
{
cout<<"Master::eightSwordKill()"<<endl;
return 8;
}
};
class NewMaster : public Master
{
public:
virtual int eightSwordKill()
{
cout<<"NewMaster::eightSwordKill()"<<endl;
return Master::eightSwordKill() * 2;
}
};
void fieldPK(Master* master, Boss* boss)
{
int k = master->eightSwordKill();
int b = boss->fight();
if( k < b )
{
cout<<"Master is killed..."<<endl;
}
else
{
cout<<"Boss is killed..."<<endl;
}
}
int main(int argc, char *argv[])
{
Boss* boss = Boss::GetInstance();
cout<<"Master vs Boss"<<endl;
Master master;
fieldPK(&master, boss);
cin.get();
cout<<"New Master vs Boss"<<endl;
NewMaster newMaster;
fieldPK(&newMaster, boss);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//17.2 父类接受子类:只能调用父类的方法。
#ifdef LESSON17_example17_2_class_parent_save_child
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
int i;
public:
Parent()
{
cout<<"Parent()"<<endl;
i = 1;
}
void print()
{
cout<<"I'm Parent..."<<endl;
cout<<"i = " << i <<endl;
}
};
class Child : public Parent
{
//若子类成员变量与父类相同。 void howToPrint(Parent* p):调用的是父类的方法,若传进来的是&child,故访问的成员变量还是父类的i
//protected:
// int i;
public:
Child()
{
cout<<"Child()"<<endl;
i = 2;
}
void print()
{
cout<<"I'm Child..."<<endl;
cout<<"i = " << i <<endl;
}
};
//调用的是父类的方法,若传进来的是&child,故访问的成员变量还是child的i
void howToPrint(Parent* p)
{
p->print();
}
void run()
{
Child child;
Parent* pp = &child;
Parent& rp = child;
child.print(); //I'm Child...
pp->print(); //I'm Parent...
rp.print();//I'm Parent...
cout << "..." <<endl;
Parent p;
howToPrint(&child);//I'm Parent...
howToPrint(&p);//I'm Parent...
}
int main(int argc, char *argv[])
{
run();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//17.1 子父类相同成员函数名:子类重写父类成员函数,父类成员函数依然存在子类之中。
#ifdef LESSON17_example17_1_class_member_same_func_name
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
void print()
{
cout<<"I'm Parent..."<<endl;
}
};
class Child : public Parent
{
public:
//子类重写父类成员函数,父类成员函数依然存在子类之中。
void print()
{
cout<<"I'm Child..."<<endl;
}
};
/*
函数重写:
1. 概念:子类定义了与父类相同原型的函数。
2. 函数重写只发生在父类与子类之间
3. 父类被重写的函数依然存继承给子类。
4. 通过作用域标识符::可以访问父类被重写的函数。
*/
int main(int argc, char *argv[])
{
Child child;
child.print();
//通过作用域标识符::可以访问父类被重写的函数。
child.Parent::print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//16.5 父类子类同名成员变量: 子类同时包含两个成员变量。存放在不同作用域
#ifdef LESSON16_example16_5_class_parent_same_member_name
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
int i;
int f;
};
//子类继承父类
class Child : public Parent
{
protected:
//子类成员变量名与父类成员变量名相同
int i;
void f()
{
cout<<"Parent::i = "<<Parent::i<<endl;
cout<<"Child::i = "<<Child::i<<endl;
cout<<"Parent::f = "<<Parent::f<<endl;
//默认调用子类的i
cout<<"i = "<<i<<endl;
}
public:
Child(int i, int j)
{
Parent::i = i;
Child::i = j;
Parent::f = i + j;
f();
}
};
/*
子类成员变量名与父类成员变量名相同
1. 子类同时包含两个成员变量。存放在不同作用域
2. 通过不同作用域来访问两个变量:2.1 Child::成员变量,2.2 Parent::成员变量
3. 默认成员变量是子类成员变量
*/
void run()
{
Child child(1, 3);
}
int main(int argc, char *argv[])
{
run();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//16.4 子类继承父类:且包好其他类成员: 构造函数调用顺序:先父母,后客人,再自己。
#ifdef LESSON16_example16_4_class_parent_child_others
#include <cstdlib>
#include <iostream>
using namespace std;
class Object
{
public:
Object(const char* s)
{
cout<<"Object()"<<" "<<s<<endl;
}
};
//父类继承对象类
class Parent : public Object
{
public:
//1. 先调用Object构造函数,
Parent(const char* s) : Object(s)
{
cout<<"Parent()"<<" "<<s<<endl;
}
};
//子类继承父类
class Child : public Parent
{
protected:
//子类成员包含其他类
Object o1;
Object o2;
public:
//1. 先调用Parent构造函数,2. 再调用o1参数初始化,2.最后调用o2参数初始化
Child() : o2("o2"), o1("o1"), Parent("Parameter from Child!")
{
cout<<"Child()"<<endl;
}
};
void run()
{
Child child;
}
/*
如果一个类继承了父类并且有其他对象作为成员,
1. 构造函数调用顺序:先父母,后客人,再自己。
*/
int main(int argc, char *argv[])
{
run();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//16.3 继承构造函数:参数列表
#ifdef LESSON16_example16_3_class_parent_child_constructor_param_list
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(const char* s)
{
cout<<"Parent(): "<<" "<<s<<endl;
}
~Parent()
{
cout<<"~Parent()"<<endl;
}
};
class Child : public Parent
{
public:
//参数列表
Child() : Parent("Parameter from Child!")
{
cout<<"Child()"<<endl;
}
~Child()
{
cout<<"~Child()"<<endl;
}
};
void run()
{
Child child;
}
int main(int argc, char *argv[])
{
run();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//16.2 继承构造函数调用顺序:先调用父类,再调用子类构造函数,析构函数相反
#ifdef LESSON16_example16_2_class_parent_child_constructor
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
Parent()
{
cout<<"Parent()"<<endl;
}
~Parent()
{
cout<<"~Parent()"<<endl;
}
};
class Child : public Parent
{
public:
Child()
{
cout<<"Child()"<<endl;
}
~Child()
{
cout<<"~Child()"<<endl;
}
};
void run()
{
//先调用父类,再调用子类构造函数,析构函数相反
Child child;
}
int main(int argc, char *argv[])
{
run();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//父类对象可以包含子类对象,访问的是父类的方法。
#ifdef LESSON16_example16_1_parent_save_child
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
protected:
const char* name;
public:
Parent()
{
cout<<"Parent()"<<endl;
name = "Parent";
}
void print()
{
cout<<"Name: "<<name<<endl;
}
};
class Child : public Parent
{
protected:
int i;
int j;
public:
Child()
{
}
Child(int i)
{
cout<<"Child(int i)"<<endl;
this->name = "Child";
this->i = i;
}
void print_child()
{
cout<<"I am Child"<<endl;
}
};
int main(int argc, char *argv[])
{
Child child(1000);
//父类对象可以包含子类对象,访问的是父类的方法。
Parent parent = child;
Parent* pp = &child;
Parent& rp = child;
//子类无法接收父类
//Child c = parent; //Error
child.print_child();
parent.print();
//父类无法访问子类成员
//parent.print_child(); //Error
pp->print();
rp.print();
cout << "sizeof(Child):" << sizeof(Child) << endl;//12
cout << "sizeof(Parent):" << sizeof(Parent) << endl;//4
cout << "sizeof(child):" << sizeof(child) << endl;//12
cout << "sizeof(parent):" << sizeof(parent) << endl;//4
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//15.4 继承:public、protected、private继承区别
#ifdef LESSON15_example15_4_jicheng_public_protected_private
#include <cstdlib>
#include <iostream>
using namespace std;
class A
{
private:
int a;
protected:
int b;
public:
int c;
A()
{
a = 0;
b = 0;
c = 0;
}
void set(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
};
class B : public A
{
public:
void print()
{
cout<<"B:"<< endl;
//父类的私有成员不能在子类直接访问
//cout<<"a = "<<a;
cout<<"b = "<< b << endl;
cout<<"c = " << c <<endl;
}
};
class C : protected A
{
public:
void print()
{
cout<<"C:"<< endl;
//cout<<"a = "<<a;
cout<<"b = "<< b << endl;
cout<<"c = " << c <<endl;
}
void setC(int a, int b, int c)
{
set(a, b, c);
}
};
class D : private A
{
public:
void print()
{
cout<<"D:"<< endl;
//cout<<"a = "<<a;
cout<<"b = "<< b << endl;
cout<<"c = " << c <<endl;
}
void setD(int a, int b, int c)
{
set(a, b, c);
//private继承,可以在内部访问父类protected,public成员。
this->b = 1;
this->c = 1;
}
};
/*
类成员访问级别设定:
1. 需要被外界访问的成员设置为:public
2. 只能在当前类访问的成员设置为:private
3. 只能在当前类和子类访问的成员设置为:protected
备注:private成员在子类依然存在,只是无法访问。
*/
/*
继承:
1. public继承:父类所有成员属性都被子类继承了,但是子类内部和外界都无法访问父类的私有成员
2. protected继承,父类public成员退化为子类的protected成员,可以在子类内部使用,不可以在外界使用。
3. private继承,父类public、protected成员退化为子类的private成员, 可以在子类内部使用,不可以在外界使用。
*/
int main(int argc, char *argv[])
{
A aa;
B bb;
C cc;
D dd;
aa.c = 100;
//public继承可以在外界直接访问父类的public成员
bb.c = 100;
//protected继承不可以在外界直接访问父类的public成员,但可以在子类内部直接访问
//cc.c = 100;
//private继承不可以在外界直接访问父类的public成员,但可以在子类内部直接访问
//dd.c = 100;
aa.set(1, 2, 3);
bb.set(10, 20, 30);
//protected继承不可以在外界直接访问父类的public成员,但可以在子类内部直接访问
//cc.set(40, 50, 60);
cc.setC(40, 50, 60);
//private继承不可以在外界直接访问父类的public成员,但可以在子类内部直接访问
//dd.set(70, 80, 90);
dd.setD(40, 50, 60);
bb.print();
cc.print();
dd.print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//15.3 public继承:父类所有成员属性都被子类继承了,可以直接访问父类的protected成员
#ifdef LESSON15_example15_3_jicheng_public
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
//父类protected成员,可以在子类直接使用。
protected:
int a;
public:
Parent()
{
a = 1000;
}
void print()
{
cout<<"a = "<<a<<endl;
}
};
class Child : public Parent
{
protected:
int b;
public:
void set(int a, int b)
{
//父类protected成员,可以在子类直接使用。
this->a = a;
this->b = b;
}
};
int main(int argc, char *argv[])
{
Parent parent;
Child child;
parent.print();
child.print();
child.set(2000, 400);
child.print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//15.2 public继承:父类所有成员属性都被子类继承了,但是无法访问父类的私有成员
#ifdef LESSON15_example15_2_jicheng_public
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
private:
int a;
public:
Parent()
{
a = 1000;
}
void print()
{
cout<<"a = "<<a<<endl;
}
};
//public继承
class Child : public Parent
{
private:
int b;
public:
void set(int a, int b)
{
//无法直接访问父类的private成员
//this->a = a;
//this->b = b;
}
};
int main(int argc, char *argv[])
{
Parent parent;
Child child;
parent.print();
//public继承可以在外部直接访问父类的public成员。
child.print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//15.1 默认继承:父类所有成员都变为子类private成员
#ifdef LESSON15_example15_1_jicheng_default
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
private:
int a;
public:
Parent()
{
a = 1000;
}
void print()
{
cout<<"a = "<<a<<endl;
}
};
//默认继承,父类的所有成员都变为private,相当于:class Child :private Parent
class Child : Parent
{
public:
void print_own(void)
{
print();
}
};
int main(int argc, char *argv[])
{
Parent parent;
Child child;
parent.print();
//无法直接访问,因为该方法是private。
//child.print();
child.print_own();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//14.5 类可以实现状态函数,并且这个状态函数可以从头再来。
#ifdef LESSON14_example_14_5_class_auto_static
#include <cstdlib>
#include <iostream>
using namespace std;
class Fib
{
private:
//使用自动变量,该变量与对象同生死,故类似静态变量。
int a1;
int a2;
public:
Fib()
{
a1 = 0;
a2 = 1;
}
//操作符()重载
int operator() ()
{
int ret = a2;
int t = a2;
a2 = a2 + a1;
a1 = t;
return ret;
}
};
int main(int argc, char *argv[])
{
Fib fib;
cout<<"Fib fib;"<<endl;
for(int i=1; i<=10; i++)
{
cout<<fib()<<endl;
}
//重新创建对象即可从头再来。
Fib fib1;
cout<<"Fib fib1;"<<endl;
for(int i=1; i<=4; i++)
{
cout<<fib1()<<endl;
}
for(int i=1; i<=4; i++)
{
cout<<fib1()<<endl;
}
cout<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//14.4 函数中静态、自动变量:静态变量有记忆功能,无法从头再来。自动变量没有记忆功能,每次都是重新开始。
#ifdef LESSON14_example_14_4_func_auto_static
#include <cstdlib>
#include <iostream>
using namespace std;
//无状态函数,无记忆功能
int fib1(int i)
{
int a1 = 0;
int a2 = 1;
int ret = a2;
while( i > 1)
{
ret = a2 + a1;
a1 = a2;
a2 = ret;
i--;
}
return ret;
}
//状态函数,有记忆功能
int fib2()
{
static int a1 = 0;
static int a2 = 1;
int ret = a2;
int t = a2;
a2 = a2 + a1;
a1 = t;
return ret;
}
int main(int argc, char *argv[])
{
for(int i=1; i<=10; i++)
{
cout<<fib1(i)<<endl;
}
for(int i=1; i<=10; i++)
{
cout<<fib2()<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//14.3 静态成员变量作用: 用于控制对象数目。单一对象系统
#ifdef LESSON14_example_14_3_class_member_static
#include <cstdlib>
#include <iostream>
using namespace std;
class Singleton
{
private:
//1.设计一个静态变量对象指针
static Singleton* cInstance;
//2. 构造函数是private,故外接无法直接创建对象。
Singleton()
{
}
public:
//3. 提供一个获取对象的方法。在方法里面控制对象个数
static Singleton* GetInstance()
{
if( cInstance == NULL )
{
cout<<"new Singleton()"<<endl;
cInstance = new Singleton();
}
return cInstance;
}
void print()
{
cout<<"I'm Singleton!"<<endl;
}
};
//静态成员指针变量初始化
Singleton* Singleton::cInstance = NULL;
void func()
{
//Singleton s; //构造函数为private,无法直接创建对象。
Singleton* s = Singleton::GetInstance();
Singleton* s1 = Singleton::GetInstance();
Singleton* s2 = Singleton::GetInstance();
cout<<s<<" "<<s1<<" "<<s2<<endl;
s->print();
}
int main(int argc, char *argv[])
{
func();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//14.2 编译器对构造函数调用:默认标准调用 Test t1(5);其他方式可能会转化为这种方式。
#ifdef LESSON14_example_14_2_constructor
#include <cstdlib>
#include <iostream>
using namespace std;
class Test
{
public:
//explicit Test(int i)//告诉编译器不要自动调用构造函数
Test(int i)
{
cout<<"Test(int i)"<<endl;
}
//explicit Test(const Test& obj) //告诉编译器不要自动调用构造函数
Test(const Test& obj)
{
cout<<"Test(const Test& obj)"<<endl;
}
~Test()
{
cout<<"~Test"<<endl;
}
};
void func()
{
//标准的使用方式
Test t1(5);
//将int类型 5赋值给对象,编译器尝试调用Test(5)再赋值给t2,这样还是浪费资源。故继续优化,直接Test t1(5);。
Test t2 = 5;
//手动调用构造函数生成一个对象,之后将对象赋值给t3。 //中间临时对象浪费资源,编译器会优化: 直接Test t1(5);
Test t3 = Test(5);
}
int main(int argc, char *argv[])
{
func();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//14.1 new malloc区别: 都是申请堆内存, new可以初始化,调用构造函数功能。
#ifdef LESSON14_example_14_1_new_mallloc
#include <cstdlib>
#include <iostream>
using namespace std;
class Test
{
private:
int i;
public:
Test()
{
cout<<"Test()"<<endl;
i = 0;
}
Test(int i)
{
cout<<"Test(int i)"<<endl;
this->i = i;
}
~Test()
{
cout<<"~Test"<<endl;
}
int getI()
{
return i;
}
};
void func()
{
//从堆里面申请sizeof(int)个字节空间。
int* p = reinterpret_cast<int*>(malloc(sizeof(int)));
//从堆里面申请int类型字节空间,并初始化。
int* q = new int();
//malloc空间若没有重新赋值则是乱码。
*p = 5;
//new空间时已经初始化,故不需要再次赋值。
//*q = 10;
cout<<*p<<" "<<*q<<endl;
free(p);
delete q;
//只是单纯地分配内存空间
Test* op = reinterpret_cast<Test*>(malloc(sizeof(Test)));
//new对象时自动调用构造函数
Test* oq = new Test;
cout<<op->getI()<<endl;
cout<<oq->getI()<<endl;
free(op);
delete oq;
}
/*
问题:malloc与free和new与delete有什么区别?
1. malloc free是库函数,new delete是关键字。
2. malloc以字节为单位申请堆内存。new以类型为单位申请对内存。
3. malloc delete只是单纯地对内存进行申请和释放。
4. new delete对基本类型申请空间时可以进行初始化。对类类型则会调用构造函数和析构函数。
*/
int main(int argc, char *argv[])
{
func();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//13.5 操作符重载:operator[],=,(),->,解释为什么只能通过成员函数方式来重载。确保为左值
#ifdef LESSON13_example13_5_operator_overload_operator_four
#include <iostream>
class X
{
public:
X(){}
X(int){} // int 类型可以被隐式转换成 X
friend bool operator<(const X& x1, const X& x2) { return true; } // 只是测试,无意义
//编译不通过:提示改函数应该为非静态成员函数。'bool operator[](const X&, const X&)' must be a nonstatic member function
//friend bool operator[](const X& x1, const X& x2) { return true; } // 只是测试,无意义
//friend bool operator()(const X& x1, const X& x2) { return true; } // 只是测试,无意义
//friend bool operator=(const X& x1, const X& x2) { return true; } // 只是测试,无意义
//friend bool operator->(const X& x1, const X& x2) { return true; } // 只是测试,无意义
};
class Y
{
public:
Y(){}
Y(int){} // int 类型可以被隐式转换成 Y
bool operator<(const Y& y) const { return true; } // 只是测试,无意义
//编译可以通过
bool operator[](const Y& y) const { return true; } // 只是测试,无意义
bool operator()(const Y& y) const { return true; } // 只是测试,无意义
bool operator=(const Y& y) const { return true; } // 只是测试,无意义
bool operator->(void) const { return true; } // 只是测试,无意义
};
/*
解释:为什么=,[],(),->等运算符只能重载成类的成员函数?
由上面的代码可以知道,如果将 =,[],(),-> 进行友元全局重载,那么就会出现 1=x; 1[x]; 1->x; 1(x);
这样的合法语句(起码编译器认为这些是合法的)--参考代码中的 if(1<x) 合法的片段,但显然这些是需要避免的,
当然这些不是我们程序员的责任,应该有语言的设计者来实现,所以,就……。
总结:1. 使用成员函数为了使对象为第一个参数。像[],(),->,=必须是对象为左值。
使用全局函数可能出现第一个参数不为对象而
*/
int main()
{
X x;
//if(1 < x) // 合法,使用友元重载函数,1 可以被隐式地转换为 X 类型 --友元函数的第一个参数
if (operator<(1, x))
{}
Y y;
//if(1 < y) // 不合法,使用成员重载函数,函数的第一个参数是 const *this,1 不能被隐式转换
//if (1.operator<(y))
if (y[1])
if (y.operator[](1))
{}
return 0;
}
// 注:编译的时候可以通过注释掉不同的代码来查看错误(即合法性),后面解释不能作为友元全局重载的原因
#endif
//13.4 操作符重载:operator&&,operator||,不要重载,违背了短路功能。
#ifdef LESSON13_example13_4_operator_overload_and_or
#include <cstdlib> //c++标准库
#include <iostream>
using namespace std;
class Test
{
int i;
public:
Test(int i)
{
this->i = i;
}
//成员函数:返回值,功能名称,参数
Test operator+ (const Test& obj)
{
Test ret(0);
cout<<"Test operator+ (const Test& obj)"<<endl;
//成员函数操作的是成员变量
ret.i = i + obj.i;
//运算结果,值返回
return ret;
}
bool operator&& (const Test& obj)
{
cout<<"bool operator&& (const Test& obj)"<<endl;
return i && obj.i;
}
};
int main(int argc, char *argv[])
{
int a1 = 0;
int a2 = 1;
if( a1 && (a1 + a2) )
{
//不会执行
cout<<"Hello"<<endl;
}
Test t1 = 0;
Test t2 = 1;
//默认功能:与&&:短路功能,第一个条件为假,第二个条件不会执行
//实际情况:操作符重载通过函数来实现,故第二个条件先执行,之后再执行第一个条件。与默认功能相违背。故不要对与&& ,或||进行重载
//if (t1.operator&&(t1.operator+(t2)))
if( t1 && (t1 + t2) )
{
cout<<"World"<<endl;
}
//或||也一样,若第一个条件为真,则第二个条件不会执行。故也不能进行操作符重载
//if (t1.operator || (t1.operator+(t2)))
//if( t1 || (t1 + t2) )
//{
// cout<<"World"<<endl;
//}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//13.3 操作符重载:operator++, //有占位符,后++ ,//无占位符,前++,至于为什么,以后理解
#ifdef LESSON13_example13_3_operator_overload_plusplus
#include <cstdlib>
#include <iostream>
using namespace std;
class Complex
{
int a;
int b;
public:
Complex(int a, int b)
{
this->a = a;
this->b = b;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
Complex operator+ (const Complex& c2);
//有占位符,后++
Complex operator++ (int);
//无占位符,前++,至于为什么,以后理解
Complex& operator++();
friend ostream& operator<< (ostream& out, const Complex& c);
};
ostream& operator<< (ostream& out, const Complex& c)
{
out<<c.a<<" + "<<c.b<<"i";
return out;
}
Complex Complex::operator++ (int)
{
//后++:先保存本身,之后自增。效率低些
Complex ret = *this;
a++;
b++;
return ret;
}
Complex& Complex::operator++()
{
//前++:直接自增,效率高
++a;
++b;
return *this;
}
Complex Complex::operator+ (const Complex& c2)
{
Complex ret(0, 0);
ret.a = this->a + c2.a;
ret.b = this->b + c2.b;
return ret;
}
int main(int argc, char *argv[])
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c2;
c2++;
++c3;
cout<<c1<<endl;
cout<<c2<<endl;
cout<<c3<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//13.2 操作符重载:只要是标准没有实现的,都可以通过重载来手动的实现 对象+,=,==,!=。
#ifdef LESSON13_example13_2_operator_overload_class_array
#include <stdio.h>
class Array
{
private:
int mLength;
int* mSpace;
public:
Array(int length);
Array(const Array& obj);
int length();
~Array();
int& operator[](int i);
Array& operator= (const Array& obj);
bool operator== (const Array& obj);
bool operator!= (const Array& obj);
};
Array::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new int[mLength];
}
Array::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
int Array::length()
{
return mLength;
}
Array::~Array()
{
mLength = -1;
printf("%08X\n", mSpace);
delete[] mSpace;
}
int& Array::operator[](int i)
{
return mSpace[i];
}
Array& Array::operator= (const Array& obj)
{
//=:删除自身空间,重新分配大小,再值拷贝
delete[] mSpace;
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
//this是对象地址,引用是对象本身
return *this;
}
bool Array::operator== (const Array& obj)
{
bool ret = true;
//先比较长度
if( mLength == obj.mLength )
{
for(int i=0; i<mLength; i++)
{
//再比较数组元素
if( mSpace[i] != obj.mSpace[i] )
{
ret = false;
break;
}
}
}
else
{
ret = false;
}
return ret;
}
bool Array::operator!= (const Array& obj)
{
return !(*this == obj);
}
int main()
{
Array a1(10);
Array a2(0);
Array a3(0);
if( a1 != a2 )
{
printf("a1 != a2\n");
}
for(int i=0; i<a1.length(); i++)
{
a1[i] = i + 1;
}
for(int i=0; i<a1.length(); i++)
{
printf("Element %d: %d\n", i, a1[i]);
}
a3 = a2 = a1;
if( a1 == a2 )
{
printf("a1 == a2\n");
}
for(int i=0; i<a2.length(); i++)
{
printf("Element %d: %d\n", i, a2[i]);
}
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//13.1 操作符重载:使用类成员函数实现,故不需要友元:friend
#ifdef LESSON13_example13_1_operator_overload_class_inner
#include <cstdlib>
#include <iostream>
using namespace std;
class Complex
{
int a;
int b;
public:
Complex(int a, int b)
{
this->a = a;
this->b = b;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
//类成员函数实现操作符重载:因为有this指针,故只要一个参数
Complex operator+ (const Complex& c2);
friend ostream& operator<< (ostream& out, const Complex& c);
};
//无法修改左操作数类时:比如标准库的ostream类的out作为左操作时,无法在ostream类添加成员函数,故,必须的用全局函数方式进行重载。
ostream& operator<< (ostream& out, const Complex& c)
{
out<<c.a<<" + "<<c.b<<"i";
return out;
}
Complex Complex::operator+ (const Complex& c2)
{
Complex ret(0, 0);
ret.a = this->a + c2.a;
ret.b = this->b + c2.b;
return ret;
}
int main(int argc, char *argv[])
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
cout<<c1<<endl;
cout<<c2<<endl;
cout<<c3<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//12.7 操作符重载:operator<<,使用全局函数方式,故需要友元:friend
#ifdef LESSON12_example12_7_operator_overload
#include <cstdlib>
#include <iostream>
using namespace std;
class Complex
{
int a;
int b;
public:
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
friend Complex operator+ (const Complex& c1, const Complex& c2);
friend ostream& operator<< (ostream& out, const Complex& c);
};
//1. 返回ostream对象:cout,是为了兼容c++标准操作。可以连续输出
//2. 第一个参数为操作符的左边,第二个为操作符的右边
ostream& operator<< (ostream& out, const Complex& c)
{
out<<c.a<<" + "<<c.b<<"i";
return out;
}
Complex operator+ (const Complex& c1, const Complex& c2)
{
Complex ret;
ret.a = c1.a + c2.a;
ret.b = c1.b + c2.b;
return ret;
}
int main(int argc, char *argv[])
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2; //+操作符重载,
cout<<c1<<endl; //<<左移操作符重载,实现可以打印一个对象数据
cout<<c2<<endl;
cout<<c3<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//12.6 操作符重载:operator+,使用全局函数方式,故需要友元:friend
#ifdef LESSON12_example12_6_operator_overload
#include <cstdlib>
#include <iostream>
using namespace std;
class Complex
{
int a;
int b;
public:
Complex(int a = 0, int b = 0)
{
this->a = a;
this->b = b;
}
int getA()
{
return a;
}
int getB()
{
return b;
}
//友元,告诉编译器,该函数为类的朋友,可以访问类的任何成员(成员变量,成员函数)
friend Complex operator+ (const Complex& c1, const Complex& c2);
};
//操作符重载,全局函数:需要两个参数
Complex operator+ (const Complex& c1, const Complex& c2)
{
Complex ret;
//通过访问类成员变量或函数是实现逻辑功能
ret.a = c1.a + c2.a;
ret.b = c1.b + c2.b;
return ret;
}
int main(int argc, char *argv[])
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2; //从使用的角度,类对象可以先加,本质是通过调用函数的方式使用的,如下
//Complex c3 = operator+(c1, c2);
cout<<"c3.a = "<<c3.getA() <<endl;
cout<<"c3.b = "<<c3.getB() <<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//12.4 类对象:相加使用函数方式
#ifdef LESSON12_example12_4_class_plus
#include <cstdlib>
#include <iostream>
using namespace std;
struct Complex
{
int a;
int b;
};
Complex add(const Complex& c1, const Complex& c2)
{
Complex ret;
ret.a = c1.a + c2.a;
ret.b = c1.b + c2.b;
return ret;
}
int main(int argc, char *argv[])
{
Complex c1 = {1, 2};
Complex c2 = {3, 4};
Complex c3 = add(c1, c2);
cout<<"c3.a = "<<c3.a<<endl;
cout<<"c3.b = "<<c3.b<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//12.3 类对象:相加编译异常
#ifdef LESSON12_example12_3_class_plus
#include <cstdlib>
#include <iostream>
using namespace std;
struct Complex
{
int a;
int b;
};
int main(int argc, char *argv[])
{
Complex c1 = {1, 2};
Complex c2 = {3, 4};
Complex c3 = c1 + c2; //对象不可以相加,编译器读不懂。故需要在类中重载+操作符。
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
#endif
//c++标准库:<iosteam> cout cin使用
#ifdef LESSON12_example12_2_stdlib
#include <iostream> //cout,cin对象使用时需要包含的头文件
using namespace std;
int main()
{
cout<<"Hello World"<<endl;
int x;
int y;
cout<<"1st Parameter: ";
cin>>x;
cout<<"2nd Parameter: ";
cin>>y;
cout<<"Result: "<<x+y<<endl;
cin.get();
cin.get(); //多个get才能停止在屏幕
return 0;
}
#endif
//c++标准库:<cstdio>前缀为c,没有.h
#ifdef LESSON12_example12_1_stdlib
#include <cstdio> //c++的标准库,与c语言<stdio.h>不同,但是实现了c语言标准库的全部内容。
using namespace std;
int main()
{
printf("Hello World!\n");
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//11.4 编译器内部运行机制,用户角度,编译器角度
#ifdef LESSON11_4_class_inner
//本质就是基于c语言的结构体struct。进行内部转换。
/*
//用户角度 //编译器角度
class Test struct Test
{ {
private: int mI;
int mI; };
public: void Test_initialize(Test *pThis, int i)
Test(int i) {
{ pThis->mI = i;
mI = i; }
} void Test_getI(Test *pThis)
{
int getI() return pThis->mI;
{ }
return mI;
}
void Test_Print()
static void Print() {
{ printf("This is class test.\n");
printf("This is class test.\n"); }
}
};
//用户角度 //编译器角度
Test a(10); Test a;
Test_initialize(&a, 10);
a.getI(); Test_getI(&a);
Test::Print(); Test_Print();
*/
#endif
//11.3 this指针: this == &对象。对象其实就是结构体,对象指针就是结构体地址。通过->访问类成员(变量,函数)
#ifdef LESSON11_3_class_this_point
#include <stdio.h>
struct C {
int i;
int j;
int k;
static int c;
};
class Test
{
int i;
int j;
int k;
static int c;
public:
Test(int i, int j, int k)
{
//对象指针:this == &t1。对象其实就是结构体,对象指针就是结构体地址。通过->访问类成员(变量,函数)
this->i = i;
this->j = j;
this->k = k;
this->print_test();
}
void print()
{
printf("Object Address: %08X\n", this);
printf("&c = %08X, c = %d\n", &c, c);
printf("&i = %08X, i = %d\n", &i, i);
printf("&j = %08X, j = %d\n", &j, j);
printf("&k = %08X, k = %d\n", &k, k);
}
void print_test()
{
printf("print_test\n");
}
};
int Test::c;
int main()
{
Test t1(0, 1, 2);
Test t2(3, 4, 5);
printf("t1 Address: %08X\n", &t1);
t1.print();
printf("t2 Address: %08X\n", &t2);
t2.print();
/*
对象大小:
1. 对象成员变量与成员函数是分开存储的
2. 普通成员变量:存储在对象中,与结构体struct相同
3. 静态成员变量:存储在全局数据区。
4. 成员函数:存储在代码段中。
5. 从用户的角度class是将属性+方法集成在同一个位置。
从计算机的角度,依然是数据段+程序段
*/
printf("\nsize:\n");
printf("sizeof(C) = %d\n", sizeof(C));
printf("sizeof(Test) = %d\n", sizeof(Test));
printf("sizeof(t1) = %d\n", sizeof(t1));
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//11.2 类静态成员:静态成员变量,静态成员函数,统计创建对象个数
#ifdef LESSON11_2_class_static_count
#include <stdio.h>
class Test
{
private:
static int cCount;
public:
static int GetCount()
{
return cCount;
}
//构造函数,创建对象时调用
Test()
{
cCount++;
}
//析构函数,销毁对象时调用
~Test()
{
cCount--;
}
};
//为类静态成员变量分配空间
int Test::cCount = 0;
void run()
{
Test ta[100];
printf("Number of Object: %d\n", Test::GetCount());
}
int main()
{
Test t1;
Test t2;
printf("Number of Object: %d\n", Test::GetCount());
run();
printf("Number of Object: %d\n", Test::GetCount());
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//11.1 类静态成员:静态成员变量,静态成员函数
#ifdef LESSON11_1_class_static
#include <stdio.h>
class Test
{
private:
//类静态成员变量
static int cI;
public:
//类静态成员函数
static int GetI()
{
return cI;
}
static void SetI(int i)
{
cI = i;
}
void print()
{
printf("cI = %d\n", cI);
}
};
//初始化类静态成员变量
int Test::cI = 0;
/*
类静态成员:
1. 类静态成员不需要创建对象即可访问,通过类名访问,有点像命名空间
2. 类静态成员共享与所有对象。
3. 类静态成员变量需要初始化分配空间。
*/
int main()
{
printf("Test::cI = %d\n", Test::GetI());
Test::SetI(5);
printf("Test::cI = %d\n", Test::GetI());
Test t1;
Test t2;
t1.print();
t2.print();
t1.SetI(10);
t2.print();
printf("Test::cI = %d\n", Test::GetI());
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//10.5 析构函数:直接调用构造函数,创建一个空白对象,很快就被销毁了
#ifdef LESSON10_5_object_destructor_order
#include <stdio.h>
class Test
{
private:
int mI;
int mJ;
const char* mS;
public:
Test()
{
printf("Test()\n");
mI = 0;
mJ = 0;
}
Test(const char* s)
{
printf("Test(const char* s)\n");
//构造一个对象,然后马上释放
Test();
mS = s;
}
~Test()
{
printf("~Test()\n");
}
void print()
{
printf("mI = %d, mJ = %d, mS = %s\n", mI, mJ, mS);
}
};
void run()
{
Test t = Test("Delphi Tang"); // Test t("Delphi Tang");
t.print();
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//10.4 析构函数:析构函数调用顺序与构造函数调用顺序相反
#ifdef LESSON10_4_object_destructor_order
#include <stdio.h>
class Test
{
private:
int mI;
public:
Test()
{
printf("Test()\n");
mI = -1;
}
Test(int i)
{
printf("Test(int i), i = %d\n", i);
mI = i;
}
Test(const Test& obj)
{
printf("Test(const Test& obj), i = %d\n", obj.mI);
mI = obj.mI;
}
//析构函数调用顺序与构造函数调用顺序相反
~Test()
{
printf("~Test(), i = %d\n", mI);
}
};
void func(Test t)
{
Test r(1);
}
void run()
{
Test t(0);
func(t);
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//10.3 析构函数:释放资源,数组类
#ifdef LESSON10_3_object_destructor_array
#include <stdio.h>
class Array
{
private:
int mLength;
int* mSpace;
public:
Array(int length);
Array(const Array& obj);
int length();
void setData(int index, int value);
int getData(int index);
~Array();
};
Array::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new int[mLength];
}
Array::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
int Array::length()
{
return mLength;
}
void Array::setData(int index, int value)
{
mSpace[index] = value;
}
int Array::getData(int index)
{
return mSpace[index];
}
//析构函数,释放资源
Array::~Array()
{
mLength = -1;
delete[] mSpace;
}
int main()
{
Array a1(10);
for(int i=0; i<a1.length(); i++)
{
a1.setData(i, i);
}
for(int i=0; i<a1.length(); i++)
{
printf("Element %d: %d\n", i, a1.getData(i));
}
Array a2 = a1;
for(int i=0; i<a2.length(); i++)
{
printf("Element %d: %d\n", i, a2.getData(i));
}
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//10.2 析构函数:释放资源,没有参数,没有返回值
#ifdef LESSON10_2_object_destructor
#include <stdio.h>
class Test
{
private:
int mI;
public:
//构造函数
Test(int i) : mI(i)
{
printf("Test(), mI = %d\n", mI);
}
//析构函数
~Test()
{
printf("~Test(), mI = %d\n", mI);
}
};
void run()
{
Test t1(1);
Test t2(2);
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//10.1 构造函数:参数列表:顺序无关,参数列表先调用,再调用构造函数
#ifdef LESSON10_1_object_constructor_param_list
#include <stdio.h>
class M
{
private:
int mI;
public:
M(int i)
{
printf("M(int i), i = %d\n", i);
mI = i;
}
int getI()
{
return mI;
}
};
class Test
{
private:
//const int c = 0;//无法直接初始化,故该变量将分配内存,退化为只读变量
const int c;
//类包含另一个类对象
M m1;
M m2;
public:
//参数列表:顺序无关,参数列表先调用,再调用构造函数
Test() : m2(3), m1(2), c(1)
{
printf("Test()\n");
}
void print()
{
printf("c = %d, m1.mI = %d, m2.mI = %d\n", c, m1.getI(), m2.getI());
}
};
/*
初始化:创建对象时
赋值:对象已经存在了
*/
int main()
{
Test t1;
t1.print();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//9.7 数组类:优化数组类拷贝构造函数
#ifdef LESSON9_class_example_array
#include <stdio.h>
class Array
{
private:
int mLength;
int* mSpace;
public:
Array(int length);
Array(const Array& obj);
int length();
void setData(int index, int value);
int getData(int index);
void destory();
};
Array::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new int[mLength];
}
//重新设计拷贝构造函数,优化
Array::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
int Array::length()
{
return mLength;
}
void Array::setData(int index, int value)
{
mSpace[index] = value;
}
int Array::getData(int index)
{
return mSpace[index];
}
void Array::destory()
{
mLength = -1;
delete[] mSpace;
}
int main()
{
Array a1(10);
for(int i=0; i<a1.length(); i++)
{
a1.setData(i, i);
}
for(int i=0; i<a1.length(); i++)
{
printf("Element %d: %d\n", i, a1.getData(i));
}
Array a2 = a1;
for(int i=0; i<a2.length(); i++)
{
printf("Element %d: %d\n", i, a2.getData(i));
}
a1.destory();
//若没有重新构造拷贝构造函数,两个对象指向同一个存储空间。导致异常
a2.destory();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//9.6 构造函数: 自动:无参构造函数,拷贝构造函数。
#ifdef LESSON9_6_object_constructor
#include <stdio.h>
class Test
{
private:
int i;
int j;
int k;
public:
void print()
{
printf("i = %d, j = %d, k = %d\n", i, j, k);
}
};
int main()
{
Test t1;
Test t2 = t1;
t1.print();
t2.print();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//9.5 构造函数: 无参构造函数,拷贝构造函数。
#ifdef LESSON9_5_object_constructor
#include <stdio.h>
/*
注意:
1. 当类中没有定义任何一个构造函数,C++编译器会为提供无参构造函数和拷贝构造函数
2. 当类中定义了任意的非拷贝构造函数时,C++编译器不会为提供无参构造函数
*/
class Test
{
public:
Test()
{
printf("Test()\n");
}
Test(const Test& obj)
{
printf("Test(const Test& obj)\n");
}
};
int main()
{
Test t1;
Test t2 = t1;
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//9.4 构造函数: 用于初始化
#ifdef LESSON9_4_object_constructor
#include <stdio.h>
class Test
{
private:
int i;
int j;
int k;
public:
Test()
{
i = 0;
j = 0;
k = 0;
}
Test(int v)
{
i = v;
j = v;
k = v;
}
void print()
{
printf("i = %d, j = %d, k = %d\n", i, j, k);
}
void print(int v)
{
printf("v = %d\n", v);
}
};
int main()
{
Test t1(4);
Test t2 = 5;
Test t3 = Test(6);
Test t4;
t4.print();
t1.print();
t2.print();
t3.print();
Test tA[3];
for(int i=0; i<3; i++)
{
tA[i].print();
}
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//9.3 构造函数: 用于初始化
#ifdef LESSON9_3_object_constructor
#include <stdio.h>
class Test
{
private:
int i;
int j;
int k;
public:
Test(int v)
{
i = v;
j = v;
k = v;
}
void print()
{
printf("i = %d, j = %d, k = %d\n", i, j, k);
}
};
/*
析构函数:
1. 与类名相同的特殊成员函数:默认:无参构造函数,拷贝构造函数
2. 可以有参数,但是没有返回值。
3. 创建对象是自动调用
4. 可以重载
5. 用于初始化
*/
int main()
{
Test t1(4);
Test t2 = 5;
Test t3 = Test(6);
t1.print();
t2.print();
t3.print();
Test tA[3] = {Test(1), Test(2), Test(3)};
for(int i=0; i<3; i++)
{
tA[i].print();
}
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
#ifdef LESSON9_2_object_init
#include <stdio.h>
class Test
{
private:
int i;
int j;
int k;
public:
void initialize()
{
i = 0;
j = 0;
k = 0;
}
void print()
{
printf("i = %d, j = %d, k = %d\n", i, j, k);
}
};
int main()
{
Test t1;
Test t2;
Test t3;
t1.initialize();
t1.print();
t2.print();
t3.print();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//9.1 类:初始化
#ifdef LESSON9_1_object_init
#include <stdio.h>
class Test
{
private:
int i;
public:
void initialize()
{
i = 0;
}
int getI()
{
return i;
}
};
int main()
{
Test t1;
Test t2;
Test t3;
t1.initialize();
t2.initialize();
t3.initialize();
printf("t1.i = %d\n", t1.getI());
printf("t2.i = %d\n", t2.getI());
printf("t3.i = %d\n", t3.getI());
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//8.4 操作类:类的精华在于封装: 将实现与接口分离。
#ifdef LESSON8_4_class_operator_test
#include <stdio.h>
class Operator
{
private:
char mOp;
double mP1;
double mP2;
public:
bool setOperator(char op);
void setParameter(double p1, double p2);
bool result(double& r);
};
//设置操作符,通过成员变量记录下来
bool Operator::setOperator(char op)
{
bool ret = false;
if( (op == '+') || (op == '-') || (op == '*') || (op == '/') )
{
ret = true;
mOp = op;
}
else
{
mOp = '\0';
}
return ret;
}
//设置操作参数,通过成员变量记录下来
void Operator::setParameter(double p1, double p2)
{
mP1 = p1;
mP2 = p2;
}
//根据已经设置的参数及操作符,做运算
bool Operator::result(double& r)
{
bool ret = true;
switch( mOp )
{
case '/':
if( (-0.000000001 < mP2) && (mP2 < 0.000000001) )
{
ret = false;
}
else
{
r = mP1 / mP2;
}
break;
case '+':
r = mP1 + mP2;
break;
case '*':
r = mP1 * mP2;
break;
case '-':
r = mP1 - mP2;
break;
default:
ret = false;
break;
}
return ret;
}
int main(int argc, char *argv[])
{
Operator op;
double r = 0;
printf("nul = %d\n", '\0');
op.setOperator('/');
op.setParameter(8, 4);
if( op.result(r) )
{
printf("Result is %f\n", r);
}
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//8.3 struct class区别:struct 默认权限为public,class 默认权限为private,
#ifdef LESSON8_1_class_struct
#include <stdio.h>
struct A
{
int i;
int getI()
{
return i;
}
};
class B
{
int i;
int getI()
{
return i;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
//struct 默认权限为public,可以直接访问
a.i = 1;
a.getI();
//class 默认权限为private,不能直接访问
//b.i = 2;
//b.getI();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//8.2 类成员作用域:类私有成员只能通过成员函数来访问。
#ifdef LESSON8_example8_2_struct_member
#include <stdio.h>
int i = 11;
struct Test
{
private:
int i;
public:
int j;
int getI()
{
i = 3;
return i;
}
};
int main()
{
int i = 2;
Test test;
test.j = 4;
printf("i = %d\n", i);
printf("::i = %d\n", ::i);
//printf("test.i = %d\n", test.i);
printf("test.j = %d\n", test.j);
//类私有成员只能通过成员函数来访问。
printf("test.getI() = %d\n", test.getI());
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//8.1 类成员: 成员变量,成员函数。
#ifdef LESSON8_example8_1_struct_member
#include <stdio.h>
struct Biology
{
bool living;
};
struct Animal : Biology
{
bool movable;
void findFood()
{
}
};
struct Plant : Biology
{
bool growable;
};
struct Beast : Animal
{
void sleep()
{
}
};
struct Human : Animal
{
void sleep()
{
printf("I'm sleeping...\n");
}
void work()
{
printf("I'm working...\n");
}
};
struct Girl : Human
{
private:
int age;
public:
void play()
{
printf("I'm girl, I'm playing...\n");
}
void print()
{
age = 22;
printf("Girl's age is %d\n", age);
play();
sleep();
work();
}
};
struct Boy : Human
{
public:
int age;
void play()
{
printf("I'm boy, I'm playing...\n");
}
void print()
{
age = 23;
printf("Boy's age is %d\n", age);
play();
sleep();
work();
}
};
/*
1. struct 默认所有成员都是public。
2. 类组成:成员变量,成员函数。
3. 类权限:private,public,protected。
*/
int main(int argc, char *argv[])
{
Girl girl;
girl.print();
Boy boy;
boy.print();
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//7.1 struct表示类
#ifdef LESSON7_1_struct_to_object
#include <stdio.h>
struct Biology //生物类
{
bool living; //生命属性
};
struct Animal : Biology //动物类,继承了生物类
{
bool movable; //属性
void findFood() //行为
{
}
};
struct Plant : Biology //植物类,继承了生物类
{
bool growable;
};
struct Beast : Animal //禽兽类 ,继承了动物类
{
void sleep()
{
}
};
struct Human : Animal //人类 ,继承了动物类
{
void sleep()
{
printf("I'm sleeping...\n");
}
void work()
{
printf("I'm working...\n");
}
};
int main(int argc, char *argv[])
{
Human human;
human.living = true;
human.sleep();
human.work();
printf("human.living = %d\n",human.living ); // 继承的属性都可以使用
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//6.5 extern "C"{}:c方式编译主要是对函数名进行编译。函数体编译还是以C++进行编译
#ifdef LESSON6_example6_5_cplusplus_extern_c
#include <stdio.h>
extern "C"
{
void func(int x)
{
const int i = 1;
int& ri = const_cast<int&>(i);
ri = 5;
printf("i = %d\n", i);
printf("ri = %d\n", ri);
}
}
void func(const char* s)
{
printf("%s\n", s);
}
int func(int a, int b)
{
return a + b;
}
/*
深入理解extern "C" {}
1. extern "c"{} 告诉c++编译器对包含的代码进行c方式编译
2. c方式编译主要是对函数名进行编译。2.1 c++编译出来: __Z4funcii, 2.2 c编译出来:_func
3. 函数体编译还是以C++进行编译
4. 比较g++ -S main.cpp -main.s(加上extern "C"), g++ -S main.cpp -main.cpp.s(去掉 extern "C")可以知道唯一不同的是函数名。
*/
int main()
{
func(1);
func("Delphi Tang");
func(1, 2);
getchar();
return 0;
}
#endif
//6.4 类型大小:
#ifdef LESSON6_example6_4_type_size
#include <stdio.h>
int main()
{
printf("sizeof(\'1\') = %d\n", sizeof('1')); //1:char
printf("sizeof(2) = %d\n", sizeof(2)); //4:int
printf("sizeof(3.0) = %d\n", sizeof(3.0)); //8:double
char c = '1';
short s = '1';
int i = '1';
long l = '1';
long long ll = '1';
c = 2; //int赋值給char,无溢出,默认转换,若溢出,则截断。
s = 2;
i = 2;
l = 2;
ll = 2;
float f = 0;
double d = 0;
f = 3.0;
d = 3.0;
getchar();
return 0;
}
#endif
//6.3 函数重载: 本质上是不同的函数:__Z4funcic, __Z4funcii
#ifdef LESSON6_example6_3_func_overload
#include <stdio.h>
//c++编译出来: __Z4funcii
void func(int a, int b)
{
printf("void func(int a, int b)\n");
}
//__Z4funcic
void func(int a, char b)
{
printf("void func(int a, char b)\n");
}
//__Z4funcci
void func(char a, int b)
{
printf("void func(char a, int b)\n");
}
//__Z4funccc
void func(char a, char b)
{
printf("void func(char a, char b)\n");
}
/*
函数重载注意事项:
1. 本质上是不同的函数。
2. c++编译出来: __Z4funcii
3. c编译出来:_func
*/
int main()
{
int a = 1;
char b = '2';
func(a, a);
func(a, b);
func(b, a);
func(b, b);
func(1, 2);
func(1, '2');
func('1', 2);
func('1', '2');
getchar();
return 0;
}
#endif
//6.2 引用:别名,存储空间
#ifdef LESSON6_example6_2_yinyong_storage_space
#include <stdio.h>
struct SV
{
int x;
int y;
int z;
};
struct SR
{
int& x;
int& y;
int& z;
};
/*
引用注意事项:
1. 引用定义时必须初始化
2. 引用是变量的别名,与变量同生死。从此,引用的值是变量的值,地址是变量的地址。
3. 引用在c++用常量指针表示的。int const *p; //指针地址不可以修改。 可以理解为内部处理的东西。
*/
int main()
{
SV sv = {1, 2, 3};
SR sr = {sv.x, sv.y, sv.z};
printf("&sv = %p\n", &sv);
printf("&sv.x = %p\n", &sv.x);
printf("&sv.y = %p\n", &sv.y);
printf("&sv.z = %p\n", &sv.z);
printf("&sr = %p\n", &sr); //有自己独立的空间
printf("&sr.x = %p\n", &sr.x); //是sv.x的引用(别名),故地址与&sv.x同
printf("&sr.y = %p\n", &sr.y);
printf("&sr.z = %p\n", &sr.z);
SV& rsv = sv;
rsv.x = 4;
rsv.y = 5;
rsv.z = 6;
printf("sv.x = %d\n", sv.x);
printf("sv.y = %d\n", sv.y);
printf("sv.z = %d\n", sv.z);
getchar();
return 0;
}
#endif
//6.1 const:const注意事项
#ifdef LESSON6_example6_1_const
#include <stdio.h>
/*
const注意事项
1. 符号表为编译器内部过程的东西,不会进入程序存储空间。
2. 用字面量初始化的const常量才会进入符号表。如:const int value = 1;//符号表。 const int value = x; //分配内存
3. volatile修饰的const常量不会进入符号表,退化为只读变量。如volatile const value = 1;
4. const 引用于初始化变量类型相同,初始化变量分配空间。如:const char c = 1, const char& rc = c; //为c分配空间
5. const 引用于初始化变量类型不同,生成新的只读变量,独立空间。如:const char c = 1; const int& i = c;//新空间
*/
int main()
{
const int x = 1; //常量,进入符号表
const int& rx = x; //为const常量取引用,导致为其分配空间。
int& nrx = const_cast<int&>(x);
nrx = 5;
printf("x = %d\n", x); //1
printf("rx = %d\n", rx); //5
printf("nrx = %d\n", nrx); //5
printf("x = %p\n", &x);
printf("rx = %p\n", &rx);
printf("nrx = %p\n", &nrx);
volatile const int y = 2; //valatile 修饰const,导致const常量不会进入符号表,退化为只读变量
int* p = NULL;
p = const_cast<int*>(&y);
*p = 6;
printf("y = %d\n", y); //6
printf("*p = %d\n", *p); //6
const int z = y; //用变量赋值,会为其分配空间
p = const_cast<int*>(&z);
*p = 7;
printf("y = %d\n", y);
printf("z = %d\n", z);
printf("*p = %d\n", *p);
char c = 'c';
char& rc = c;
const int& trc = c; //const 引用与初始化变量类型不一样,会为其重新分配空间。
rc = 'a';
printf("c = %c\n", c);//a
printf("rc = %c\n", rc);//a
printf("trc = %c\n", trc);//c
printf("&c = %p\n", &c);
printf("&rc = %p\n", &rc);
printf("&trc = %p\n", &trc);
getchar();
return 0;
}
#endif
//5.8 C++强制类型转换:reinterpret_cast指针之间强制类型转换
#ifdef LESSON5_8_type_conversion_reinterpret_cast
#include <stdio.h>
int main()
{
int i = 0;
char c = 'c';
int* pi = &i;
char* pc = &c;
printf("pc = %p\n", pc);
printf("pi = %p\n", pi);
//int类型指针转换为char类型指针
printf("int* to char*\n");
pc = reinterpret_cast<char*>(pi);
printf("pc = %p\n", pc);
printf("pi = %p\n", pi);
//char类型指针转换为int类型指针
printf("\nchar* to int*\n");
pc = &c;
pi = reinterpret_cast<int*>(&c);
printf("pc = %p\n", pc);
printf("pi = %p\n", pi);
//c = reinterpret_cast<char>(i); // Oops!
pi = reinterpret_cast<int*>(0x12345678); //不安全
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.7 C++强制类型转换:const_cast去除变量const属性
#ifdef LESSON5_7_type_conversion_const_cast
#include <stdio.h>
//xxx_cast<TYPE>(表达式)
#include <stdio.h>
int main()
{
const int& j = 1;
const int x = 2;
//const_cast去除变量const属性
int& k = const_cast<int&>(j);
int& y = const_cast<int&>(x);
k = 5;
printf("k = %d\n", k);
printf("j = %d\n", j);
y = 8;
printf("x = %d\n", x);
printf("y = %d\n", y);
printf("&x = %p\n", &x);
printf("&y = %p\n", &y);
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.6 C++强制类型转换:基本数据类型之间转换,不能用于指针。
#ifdef LESSON5_6_type_conversion_static_cast
#include <stdio.h>
//xxx_cast<TYPE>(表达式)
int main()
{
int i = 0x12345;
float f = 3.456f;
double d = 2.3412341;
char c = 'c';
int* pi = &i;
char* pc = &c;
//int类型转换为char类型
c = static_cast<char>(i);
printf("c = %x\n", c);
c = static_cast<char>(f);
printf("c = %x\n", c);
c = static_cast<char>(d);
printf("c = %x\n", c);
//不能用于指针转换
//pc = static_cast<char*>(pi);
printf("pc = %p\n", pc);
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.5 强制类型转换: c语言可任意类型进行转换,简单灵活,但是过于粗暴
#ifdef LESSON5_5_type_conversion
#include <stdio.h>
typedef void(PF)(int);
struct Point
{
int x;
int y;
};
//c语言可以任意类型进行转换,简单灵活,但是过于粗暴,容易出问题。
//(TYPE)(表达式)或者 TYPE(表达式)
/*
难以解决的bug思路:
1. 运算符优先级:
2. 多线程编程,各线程之间的交互
3. 强制类型转换
*/
int main()
{
int v = 0x12345;
PF* pf = (PF*)v;
char c = char(v);
Point* p = (Point*)v;
printf("p->x = %d\n", p->x);
printf("p->y = %d\n", p->y);
//程序挂掉
pf(v);
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.4 关键字:namespace 命名空间的使用。
#ifdef LESSON5_4_keyword_namespace
#include <stdio.h>
#include <stdio.h>
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
/*
命名空间使用:
1. 使用整个命名看空间:using namespace name;
2. 使用命名空间的变量:using name::variable;
3.使用默认命名看空间变量:::variable;
*/
int main()
{
//使用命名空间
using namespace First;
using Second::Internal::P;
printf("i = %d\n", i);
i = 11;
printf("i = %d\n", i);
printf("i = %d\n", Second::i);
P p = {2, 3};
printf("p.x = %d\n", p.x);
printf("p.y = %d\n", p.y);
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.3 关键字:namespace 命名空间解决标识符冲突问题。
#ifdef LESSON5_3_keyword_namespace
#include <stdio.h>
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.2 关键字:new delete 初始化
#ifdef LESSON5_2_keyword_new_delete_init
#include <stdio.h>
struct Student {
char *name;
int age;
};
int main()
{
int* pi = new int(1);
float* pf = new float(2.0f);
char* pc = new char('c');
//分配空间时就进行初始化
Student *s = new Student{"KUI", 25};
printf("*pi = %d\n", *pi);
printf("*pf = %f\n", *pf);
printf("*pc = %c\n", *pc);
printf("name = %s, age = %d\n", s->name, s->age);
delete pi;
delete pf;
delete pc;
delete s;
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//5.1 关键字:new delete 动态分配内存,与mallo有区别
#ifdef LESSON5_1_keyword_new_delete
#include <stdio.h>
/*
关键字new 与malloc函数区别:
1. 关键字new是c++的一部分,malloc函数是c库提供的函数
2. new以类型为单元,malloc以字节为单元进行内存分配
3. new申请类型变量可以进行初始化,malloc不可以。
*/
int main()
{
//new一个对象,以类型为单元
int* p = new int;
*p = 5;
*p = *p + 10;
printf("p = %p\n", p);
printf("*p = %d\n", *p);
delete p;
//申请数组内存单元
p = new int[10];
for(int i=0; i<10; i++)
{
p[i] = i + 1;
printf("p[%d] = %d\n", i, p[i]);
}
//删除数组内存单元
delete[] p;
printf("Press any key to continue...");
getchar();
return 0;
}
#endif
//4.7 C++与C语言相互调用:c++编译器以c语言方式编译代码: extern "C" {}
#ifdef LESSON4_example4_1_1
#include <stdio.h>
//example4-1
/*
//add.h
int add(int a, int b);
//add.c
#include "add.h"
int add(int a, int b)
{
return a + b;
}
//main.cpp
#include <stdio.h>
extern "C"
{
#include "add.h"
}
int main()
{
printf("1 + 2 = %d\n", add(1, 2));
return 0;
}
*/
//example4-2
/*
//add.h
int add(int a, int b);
//add.cpp
extern "C"
{
#include "add.h"
int add(int a, int b)
{
return a + b;
}
}
//main.c
#include <stdio.h>
#include "add.h"
int main()
{
printf("1 + 2 = %d\n", add(1, 2));
return 0;
}
*/
int main(int argc, char *argv[])
{
printf("cplusplus test\n");
//example4-1
//opem cmd
//1. gcc -c add.c -o add.o
//2. g++ main.cpp add.o
//3. a.exe
//example4-2
//opem cmd
//1. g++ -c add.c -o add.o
//2. gcc main.cpp add.o
//3. a.exe
getchar();
return 0;
}
#endif
//4.6 C++与C语言相互调用:c++编译器以c语言方式编译代码: extern "C" {}
#ifdef LESSON4_6_cplusplus_extern_c
#include <stdio.h>
#include <string.h>
//extern "C" {
// //c语言方式编译
//}
//若编译器为c++则使用c语言方式编译
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
//C++ 内置宏定义
#ifdef __cplusplus
extern "C" {
#endif
int func(int a, int b)
{
return a + b;
}
//c语言规则没有重载概念
#if 0
int func(const char* s)
{
return strlen(s);
}
#endif
#ifdef __cplusplus
}
#endif
//C与C++并不对立,可以同时存在于同一个项目。就像C与汇编语言一样。
int main(int argc, char *argv[])
{
int c = func(1, 2);
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//4.5 c++编译器以c语言方式编译代码: extern "C" {}
#ifdef LESSON4_5_cplusplus_extern_c
#include <stdio.h>
#include <string.h>
//extern "C" {
// //c语言方式编译
//}
//若编译器为c++则使用c语言方式编译
#ifdef __cplusplus
extern "C" {
#endif
int func(int x)
{
return x;
}
//int func(int x, int y)
//{
// return x;
//}
#ifdef __cplusplus
}
#endif
int main(int argc, char *argv[])
{
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//4.4 函数重载:函数指针类型必须匹配
#ifdef LESSON4_4_func_overload_func_point
#include <stdio.h>
#include <string.h>
int func(int x) // int(int a)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
typedef int(*PFUNC)(int a); // int(int a)
int main(int argc, char *argv[])
{
int c = 0;
//int (*p) int(a); //直接定义函数指针
//p = func;
PFUNC p = func;
c = p(1); //int func(int x) //函数类型与重载的函数类型相同。
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//4.3 函数重载:重载+默认参数,有二义性不要同时使用
#ifdef LESSON4_3_func_overload_default_param
#include <stdio.h>
#include <string.h>
#if 0
int func(int a, int b, int c = 0)
{
return a * b * c;
}
#else
int func(int a, int b)
{
return a + b;
}
#endif
int main(int argc, char *argv[])
{
int c = 0;
c = func(1, 2); // 存在二义性,调用失败,编译不能通过
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
#ifdef LESSON4_2_func_overload
#include <stdio.h>
#include <string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
int func(int a, const char* s)
{
return a;
}
int func(const char* s, int a)
{
return strlen(s);
}
/*
* 函数重载至少满足:1. 参数个数不同,2. 参数类型不同,参数顺序不同
*/
int main(int argc, char *argv[])
{
int c = 0;
c = func(1);
printf("c = %d\n", c);
c = func(1, 2);
printf("c = %d\n", c);
c = func("ab");
printf("c = %d\n", c);
c = func("ab", 1);
printf("c = %d\n", c);
c = func(1, "ab");
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//4.1 函数重载: 一个函数名搭配不同参数
#ifdef LESSON4_1_func_overload
#include <stdio.h>
#include <string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
//一个函数名搭配不同参数,就用不同的实体。
int main(int argc, char *argv[])
{
int c = 0;
c = func(1);
printf("c = %d\n", c);
c = func(1, 2);
printf("c = %d\n", c);
c = func("12345");
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//3.5 函数:占位+默认参数结合
#ifdef LESSON3_5_set_position
#include <stdio.h>
//占位参数不使用,用于兼容C语言,及拓展
int func(int a, int b, int = 0)
{
return a + b;
}
int main(int argc, char *argv[])
{
printf("func(1, 2, 3) = %d\n", func(1, 2)); //少一个参数也正常。
printf("func(1, 2, 3) = %d\n", func(1, 2, 3));
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//3.4 函数:占位
#ifdef LESSON3_4_set_position
#include <stdio.h>
//占位参数不使用,用于兼容C语言,及拓展
int func(int a, int b, int)
{
return a + b;
}
int main(int argc, char *argv[])
{
//printf("func(1, 2, 3) = %d\n", func(1, 2)); //少一个参数将异常。
printf("func(1, 2, 3) = %d\n", func(1, 2, 3));
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//3.3 函数参数:声明时指定默认参数
#ifdef LESSON3_3_func_default_param
#include <stdio.h>
//一旦指定默认参数后,后面所有参数都必须使用默认参数。
int add(int a, int b = 0, int c = 0, int d = 0)
{
return a + b + c;
}
int main(int argc, char *argv[])
{
printf("add(2) = %d\n", add(2));
printf("add(1, 2) = %d\n", add(1, 2));
printf("add(1, 2, 3) = %d\n", add(1, 2, 3));
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//3.3 函数参数:声明时指定默认参数
#ifdef LESSON3_2_func_default_param
#include <stdio.h>
//声明时指定默认参数
int mul(int x = 1);
int main(int argc, char *argv[])
{
printf("mul(2) = %d\n", mul(2));
printf("mul(-2) = %d\n", mul(-2));
printf("mul() = %d\n", mul());
printf("Press enter to continue ...");
getchar();
return 0;
}
//定义时不再指定
int mul(int x)
{
return x * x;
}
#endif
//3.2 内联函数:不一定申请成功
#ifdef LESSON3_example3_1
#include <stdio.h>
//加上 __attribute__((always_inline))则一直内联。该编译器为c++特有的,为了兼容性,最好不要使用。
inline int f_inline(int a, int b) __attribute__((always_inline));
int g_no_inline(int a, int b);
//预处理+编译:compilation: g++ -S main.cpp -o main.s //c代码变为汇编代码: 查看是否用函数调用。
/*
* 注意:内联函数体应该短小,类似中断。过大的话则失去设计的意义。
*/
int main(int argc, char *argv[])
{
int r1 = f_inline(1, 2); //内联实现机制与const类似。都是进入符号表
int r2 = g_no_inline(1, 2);
printf("Press enter to continue ...");
getchar();
return 0;
}
int f_inline(int a, int b)
{
return a < b ? a : b;
}
int g_no_inline(int a, int b)
{
return a < b ? a : b;
}
#endif
//3.1 内联函数:比函数少开销,比宏安全。
#ifdef LESSON3_1_inline_micro
#include <stdio.h>
#define FUNC(a, b) ((a) < (b) ? (a) : (b))
int func(int a, int b)
{
return a < b ? a : b;
}
//添加环境变量:C:\Program Files (x86)\Dev-Cpp\MinGW32\bin
//预处理:prepressing: armcpp/g++ -E main.cpp -o main.i //只是替换:1.#define 删除,展开宏, 2. 处理条件预编译指令#if #ifdef,3.删除注释
//预处理+编译:compilation: armcpp/g++ -S main.cpp -o main.s //c代码变为汇编代码: 1.词法分析,语法分析。
//预处理+编译+汇编:assembly: armcpp/g++ -c mian.cpp -o main.o //c代码变为目标文件.o(二进制) :1.把汇编指令翻译为机器指令。
//预处理+编译+汇编+链接:linking: armcpp/g++ main.cpp -o main //c文件变为可执行文件(二进制) :1.链接库文件。
int main(int argc, char *argv[])
{
int a = 1;
int b = 3;
int c = func(++a, b); //内联函数:参数,返回值检查(且无副作用),完全可以代替 宏定义
printf("inline:\n");
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
a = 1;
b = 3;
c = FUNC(++a, b);
printf("micro:\n");
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//2.9 引用:引用作为返回值,需要考虑作用域
#ifdef LESSON2_example2_1
#include <stdio.h>
int& f()
{
static int a = 0;
return a;
}
int& g()
{
int a = 1;
//变量内存已经被销毁了,故通过内存擦操作则会异常
return a;
}
int main()
{
int a = g();
int& b = g();//返回a的引用,but 已释放的 a 可以再被引用
f() = 10;//静态变量的引用 可以作为 左值,右值
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("f() = %d\n", f());
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//2.8 引用: 引用等效于常指针 int* const a;
#ifdef LESSON2_8_yinyong_storage_space
#include <stdio.h>
//举例:等效,为了实用性,c++内部屏蔽掉
//void func(int &a) // void func(int * const a) //地址不能修改,变量值可以修改
//{ //{
// a = 5; // *a = 5;
//} //}
struct TRef
{
int& a; //引用int &a等效与常指针 int* const a;
int& b;
};
int main(int argc, char *argv[])
{
//char &a,char &b效果大小还是8
printf("sizeof(TRef) = %d\n", sizeof(TRef)); //8, 引用等效于常指针,故大小与指针相同
int a = 1;
int b = 2;
int c = 3;
TRef rA = {a, b};
printf("&a = %08X\n", &a);
printf("&b = %08X\n", &b);
printf("&rA = %08X\n", &rA);
printf("sizeof(TRef) = %d\n", sizeof(TRef)); //32bit系统中,8
printf("a = %d\n", a );
rA.a = 10;
printf("&rA.a = %p\n", &rA.a );
printf("a = %d\n", a ); //a 的值被修改了
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//2.7 引用:const结合, const 声明引用=数值
#ifdef LESSON2_7_yinyong_const
#include <stdio.h>
int main(int argc, char *argv[])
{
const int &b = 1; //情况二: const 声明引用=数值, 编译器为其分配内存空间,作为只读变量,int &b = 1;编译不通过,不能这么用
int* p = (int*)&b;
//b = 5;
*p = 5; //只读变量通过修改地址可以修改内容
printf("b = %d\n", b);
printf("Press enter to continue ...");
getchar();
}
#endif
//2.6 引用:const结合, const 声明引用=变量
#ifdef LESSON2_6_yinyong_const
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 4;
const int &b = a; //情况一: const 声明引用=变量 , 给变量加上只读属性(操作引用时)
int* p = (int*)&b; //若不去除const属性 则修改不了
//b = 5; //给普通变量加上只读属性。 错误警告
*p = 5; //通过地址修改可以改变 ,等效==> //*((int*)&b) = 5;
//*((int*)&b) = 5;
printf("a = %d, b = %d\n", a, b); //5, 5
printf("&a = %p, &b = %p\n", &a, &b); //地址相同
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//2.5 引用:代替指针:更好的可读性,实用性
#ifdef LESSON2_5_yinyong
#include <stdio.h>
//void swap(int *a, int *b)
//{
// int t;
// t = *a;
// *a = *b;
// *b = t;
//}
void swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
int main(int argc, char *argv[])
{
int a = 4;
int b = 5;
//注意引用变量的生命周期,即作用域
swap(a, b); //引用 某种程度可以代替指针 ,相对于 swap(&a, &b)可读性好很多
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//2.4 引用:变量的别名
#ifdef LESSON2_4_yinyong
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 4;
int& b = a; // 普通引用定义时必须初始化,作为形参就不需要
//引用就是取别名 ,就是原有变量的另一个名称,与该变量有相同的值,相同的地址。 eg: 陈腾奎,KUI
b = 5;
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("&a = %08X\n", &a);
printf("&b = %08X\n", &b);
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//2.3 三目运算符:返回值可以作为左值
#ifdef LESSON2_3_three_eyes
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
//C语言:三目运算符不能做左值
(a < b ? a : b) = 3; //C++:三目运算符可以做左值, 三目运算符?:返回都是变量可以做左值,返回数值则不可以
printf("a = %d, b = %d\n", a, b); //(返回的是引用 &)
printf("Press enter to continue ...");
getchar();
}
#endif
//2.1 布尔类型:true:非0, false:0,真正的
#ifdef LESSON2_1_bool
#include <stdio.h>
int main(int argc, char *argv[])
{
int a;
bool b = true; //布尔只有 假 false = 0 , 真: true = 非 0 占用一个bit (没优化则占用一个byte,优化可能只占用一个bit,这个取决于编译器)
printf("true = %d, false = %d\n", true, false); // true=1, false=0
printf("b = %d, sizeof(b) = %d\n", b, sizeof(b)); //b = 1, sizeof(b) = 1大小为一个字节
b = 3; // -5,
a = b;
printf("a = %d, b = %d\n", a, b); //a = 1, b = 1
a = 10;
b = a;
printf("a = %d, b = %d\n", a, b); //a = 10, b = 1
b = 0;
printf("b = %d\n", b);
b = false;
printf("b = %d\n", b);
b++; //可以++, 不能--
printf("b = %d\n", b); // b = 1
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.8 const:宏:作用域
#ifdef LESSON1_8_example1_2_const_micro
#include <stdio.h>
void f()
{
#define a 3
const int b = 4;
//#undef a //作用域解除
}
void g()
{
printf("a = %d\n", a);
//printf("b = %d\n", b);
}
int main(int argc, char *argv[])
{
f();
g();
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.7 const:与宏的区别:const常量编译阶段处理的,可以作为数组参数,变量是运行时才确定的,不可以作为数组参数。
#ifdef LESSON1_7_example1_1_const_array
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
//#define a 1 //等效,预编译阶段处理的
const int a = 1; //该常量与宏定义类似,但是又不同,有自己的作用域,还有数据类型
const int b = 2; //数组_常量 预编译是 可以通过 ,c语言是变量,错误了
//b = 10;
//const常量编译阶段处理的,故常量(符号表获取)作为数组参数是没问题的,若用变量作用数组元素,编译阶段出问题了。(因为变量是在运行阶段才知道值的)。
char array[a + b] = {0};
int i = 0;
memset(array, 0xaa, sizeof(array));
for(i=0; i<(a+b); i++)
{
printf("array[%d] = 0x%x\n", i, array[i] & 0xff);
}
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.6 函数:C++要有确定的参数,确定的返回值
#ifdef LESSON1_6_func_param_return
#include <stdio.h>
//f(i) // c ++函数必须的指明类型名 ,c语言不指定默认为int
void f(int i)
{
printf("i = %d\n", i);
}
//g() //默认类型为void
int g(void)
{
return 5;
}
int main(int argc, char *argv[])
{
printf("Begin...\n");
f(10);
printf("g() = %d\n", g());
printf("End...\n");
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.5 结构体:是新类型,不需要加上struct,直接:结构体名 变量名
#ifdef LESSON1_5_struct_new_type
#include <stdio.h>
struct Student
{
const char* name;
int age;
};
int main(int argc, char *argv[])
{
//新类型定义变量
Student s1 = {"Delphi", 30};
Student s2 = {"Tang", 30};
Student KUI = {"KUI", 23};
printf(" name: %s\n age = %d\n",KUI.name ,KUI.age ); //结构体变量 ‘. ’ 指针 ' -> '
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.4 const :真正的常量,编译器让其进入符号表,故不为该只读变量分配内存。
#ifdef LESSON1_4_const
#include <stdio.h>
/*
* c语言const属性
* 把类型int去掉,若const修饰p,则p不可变。若const修饰*p,则*p不可变
*/
//const int *p; //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变
//int const *p; //const 修饰*p,p 是指针, *p 是指针指向的对象,不可变
//int *const p; //const 修饰 p, p 不可变, p 指向的对象可变
//const int *const p; //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象都不可变
int main(int argc, char *argv[])
{
const int c = 2;
int* p = (int*)&c;
int array[c];
printf("Begin...\n");
*p = 5; //怎么修改都没有用
//c = 3; //c++ const为常量 :不能修改 but c语言可以修改
printf("c = %d\n", c); //0, 直接从符号表里取出 //若发现遇到extern,或&取址符号,则分配相应内存,但是c常量还是无法赋值。
printf("*p = %d\n", *p); //5, 操作的是变量的内存空间
printf("End...\n");
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.3 全局变量:不能重复定义,拒绝二义性。
#ifdef LESSON1_3_global_variable
#include <stdio.h>
//int g_var; //重复定义,而c语言不会提示该错误。
//全局变量重复定义 c++不支持,but c语言可以 非这样不可 得用extern int g_var;
extern int g_var;
int g_var = 1;
int main(int argc, char *argv[])
{
printf("Begin...\n");
printf("g_var = %d\n", g_var);
printf("End...\n");
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.2 register寄存器:register变量可以取址,C++编译器自动优化退化为普通变量。
#ifdef LESSON1_2_register
#include <stdio.h>
int main(int argc, char *argv[])
{
//C语言:变量属性:register,auto,static
register int c = 0;
register int r = 1;
printf("Begin...\n");
printf("&c = %08X\n", &c); //寄存器变量有地址了,表明并没有存储在寄存器而是内存。
//register变量可以取址 (自动优化:退化为普通变量) ,C++有自动优化功能,于是register没什么用了,只是为了兼容C
printf("End...\n");
printf("Press enter to continue ...");
getchar();
return 0;
}
#endif
//1.1 变量初始化:用到时再定义,如for定义的变量,作用域只在for循环体内。
#ifdef LESSON1_1_variable_init
#include <stdio.h>
/*
* 完全兼容c语言
*/
int main(int argc, char *argv[])
{
int c = 0;
for(int i=0; i<10; i++) //c++ 用到再定义 c语言不可以
{
for(int j=0; j<10; j++)
{
c += i * j;
}
//printf("j = %d\n", j); //作用域只在for循环
}
printf("Press enter to continue ...");
getchar(); //暂停,获取一个字符
return 0;
}
#endif