C++之高级编程20170914
/******************************************************************************************************************/
一、C++高级编程_抽象类_概念
前面有virtual 后面=0,则为纯虚函数,一个类里面有纯虚函数就变成了抽象类:
class Human {
private:
int a;
public:
virtual void eating(void) = 0;//纯虚函数
virtual void wearing(void) = 0;//纯虚函数不需要定义
virtual void driving(void) = 0;
};
抽象类不能用来实例化对象,若子类没有覆写所有的纯虚函数,则子类还是抽象类,即其实现的子类必须全部实现纯虚函数才可以实例化对象
成员函数都是纯虚函数则称为接口,接口是特殊的抽象类
作用:向下(派生类)定义好框架,向上提供统一的接口
(与java一致)
/******************************************************************************************************************/
二、C++高级编程_抽象类界面
一个程序由多个人编写,分为:
应用编程:使用类(使用类生成的动态库)
类编程:提供类,比如Englishman, Chinese
1.这样修改了类,只是动态库修改了,应用程序不用修改,实现了分层
使用动态库:
Makefile 书写:
Human: main.o libHuman.so
g++ -o $@ $< -L./ -lHuman
#-L./ 在当前目录下来查找动态库
#-lHuman 把Human动态库编译(连接)进去
%.o : %.cpp
g++ -fPIC -c -o $@ $<
#编译时加上-fPIC PIC是位置无关码
libHuman.so : Englishman.o Chinese.o Human.o
g++ -shared -o $@ $^
#-shared生成动态库
执行时:
执行时,要指定库路径,不然出错
设置LD_LIBRARY_PATH 表示运行时去哪里找需要的库
例:LD_LIBRARY_PATH=./
./Human
LD_LIBRARY_PATH等于当前目录,然后执行程序
2.这样修改了类,只是动态库需要修改(make libHuman.so来需改动态库),应用程序不用修改,实现了分层。
但是如果修改了应用程序依赖的头文件,那么就需要修改应用程序,也就是需要重新编译应用程序,那么如何实现依赖的头文件不变呢,就需要相对固定的头文件,也就是头文件是相对固定的抽象类(接口),同时对于需要的其他依赖可以使用函数声明放到该头文件(或称抽象类的界面)
app
---------------------------------
Human.h
---------------------------------
Englishman Chinese
3.对于基类的析构函数不应该使用纯虚函数,因为这样会使得其派生类还要再实现基类的析构函数
Human& e = CreateEnglishman("Bill", 10, "sfwqerfsdfas");
Human& c = CreateChinese("zhangsan", 11, "beijing");
Human* h[2] = {&e, &c};
int i;
for (i = 0; i < 2; i++)
test_eating(h[i]);
delete &e;//虽然转型了,但是原本还是派生类Englishman对象,所以还会调用派生类Englishman的析构函数
delete &c;
/******************************************************************************************************************/
三、C++高级编程_函数模板_引入
把变量类型作为参数来定义一个函数,就称为函数模版
定义:
template<类型参数表>
返回值 函数名(数据参数表)
{
函数模板定义体;
}
例:
template<typename T>
//T表示变量类型,即T等价于类型
T& mymax(T& a, T& b)
//模版要求a ,b两个参数类型相同
{
cout<<__PRETTY_FUNCTION__<<endl;//打印出函数的具体名称
return (a < b)? b : a;
}
int main(int argc, char **argv)
{
int ia = 1, ib = 2;
float fa = 1, fb = 2;
double da = 1, db = 2;
mymax(ia, ib);//生成不同的函数
mymax(fa, fb);//通过函数模版得到具体函数的过程称为模版的实例化或模版的具体化
mymax(da, db);//得到是三个不同的函数,三个调用对应的是不同的函数
return 0;
}
1. 函数模板只是编译指令,一般写在头文件中;
2. 编译程序时,编译器根据函数的参数来“推导”模板的参数;然后生成具体的模板函数
1).示例代码:
int a; int b; mymax(a, b);
编译器根据函数参数a,b推导出模板参数为int,所以把模板中的T绑定为int;
编译程序时生成如下函数:
int& mymax(int& a, int& b)
{
return (a<b)?b:a;
}
2).参数推导过程:
首先要苛刻的类型匹配(与函数模版匹配,是否满足函数模版要求),如果无法苛刻类型匹配,那么参数要进行有限的类型转换,如果进行有限的类型转换后还是不能匹配,那么就会出错。
3.有限的类型转换:
1).函数模版只支持2中隐式类型转换:
I、const转换:函数传参为非const引用/指针,它可以隐式转换为const引用/指针,但是反过来传参是const形参是非const就不行。
II、数组或函数指针的转换:
传参为数组(形参为指针)可以隐式转换为“指向第一个元素的指针”(当形参为普通元素类型则数组转换为数组最后一个元素)
传参为“函数的名字”时,它隐式转换为“函数指针”
int main(int argc, char **argv)
{
char a[]="ab";
char b[]="cd";
mymax(a, b); /* T=char[3] //T推导为char[3]*/
mymax2(a, b);
char a2[]="abc";
char b2[]="cd";
//mymax(a2, b2); /* mymax(char[4], char[3]), 无法推导出T: mymax(char& [4], char& [3]), 因为两个参数类型不一样 //转换后类型不一样,所以有错*/
mymax2(a2, b2); /* mymax2(char[4], char[3]), 推导: mymax2(const char *, const char *); //T等价于char *,即都转换为“指向第一个元素的指针”,所以一致也就没错*/
test_func(f1);//传参是函数名字和传参是函数名字取址是一样的,因为可以转换
test_func(&f1);
return 0;
}
2).其他隐式转换都不支持,比如:算术转换,派生类对象向上转换。
3).参数类型为传值时,忽略实参的const,volatile等属性,因为传值时,会临时生成一个变量,此变量可读可写
/******************************************************************************************************************/
四、C++高级编程_函数模板_重载
函数模板的重载:
1.函数选择过程:
1).先列出候选函数,包括普通函数、参数推导成功的模板函数
2).这些候选函数,根据“类型转换”来排序(转换越小甚至没转换排前面更优先,注意:模板函数只支持有限的类型转换):
3).如果某个候选函数的参数,跟调用时传入的参数更匹配,则选择此候选函数
4).如果这些候选函数的参数匹配度相同
I、如果有一个非模板函数(普通函数),则选择它(优先选择普通函数)
II、如果只有模板函数,则选择“更特化”的模板函数(“更特化”:参数匹配更特殊,更具体,更细化)
例:
const T& mymax(T& a, T& b)//可以准换普通类型,也可以转换指针类型
const T mymax(T * a, T* b)//只能转换指针,即参数只能是指针,所以这个更细化,即更特化
III、否则,最后导致“二义性”错误(ambiguous)
例:
int *p1=&ia;
int *p2=&ib;
cout<<mymax(p1, p2)<<endl;//普通函数无法把指针转换为引用,函数模版可以,所以使用2即函数模版(当然如果有函数模版是指针形参的当然选择那个函数模版):
//输出使用结果表示使用的是函数模版:template<typename T>
const T& mymax(T& a, T& b)
{//推导中,引用只是别名而已,所以没有任何关系,即没有任何影响
cout<<"2: "<<__PRETTY_FUNCTION__<<endl;
return (a < b)? b : a;
}
注意,指针表示的解读:
从右往左读,遇到p就替换成"*p is a"遇到*就替换成"point to"
例:
const int *p //p指向只读的int
引用也是一样的,例:
int a=1;
const int &b=a;//定义了常量引用b,通过b无法修改a
再比如:
int *q=&a;
const (int *)p=q;//无法通过p修改q,但是p所指内容可修改,即q可修改
//const 修饰的是int *
好文:理解模板参数推导,可以看下文:
http://blog.csdn.net/coolmeme/article/details/43986163
要深入: C++标准之(ravalue reference) 右值引用介绍
http://www.cnblogs.com/opangle/archive/2012/11/19/2777131.html
/******************************************************************************************************************/
五、C++高级编程_类模板
1. 声明(一般写在头文件中)
template<typeclass T>
class AAA {
private:
T obj; /* 使用T表示某种类型*/
public:
void test_func(T& t);
.....
};
2. 定义
template<typeclass T>
void AAA<T>::test_func(T& t) { .... }
3.使用类模板
1).用到时才生成代码(具体代码)
用到时再实例化:
AAA<int> a;
AAA<double> b;
2).一次性事先生成好代码
事先实例化:
template AAA<int>;
再使用:
AAA<int> a;
4.定做(类似重载)
1). 声明
template<>
class AAA<int> {
......
public:
void test(void);
};
2). 定义
void AAA<int>::test(void) {...}
5.链表操作,以及容器中大量使用到了类模版
/******************************************************************************************************************/
六、C++高级编程_异常
多层次函数调用时,当最深层次的函数出错时,最上层的函数想知道错误,就必须一级一级来判断返回值,这样就太麻烦了,如何解决这个问题?
c语言中使用长跳转来解决这个问题。
在c++中,使用异常来处理:
当A调用B,B调用C时,函数A捕捉函数c发出的异常时:
谁捕捉异常?A
谁抛出异常?C
捕捉到异常后怎么处理?随A
1.抛出异常:
B()
{
throw 某个对象:
}
2.捕捉处理:
A()
{
try{
B();
} catch(类形 对象)
{
//处理
}
}
一旦发生异常就会中断当前的执行,直接调到catch中
3.多种类型的异常,可以有多种捕捉,但是太多异常的话,就麻烦了,此时使用省略号来通配,
4.由于catch需要事先知道哪些类型,就使用声明,声明可能扔出哪些异常(声明里要包含内部可能出现的所有类型的异常,不包含的异常属于意料之外的异常,即使有捕捉处理但发生时还是捉不到,异常会继续向上抛出最终到系统默认处理(执行两个函数)),
5.即对于意料之外的异常,会执行2个函数:
"unexpected"函数(可以自己提供),
"terminate"函数(可以自己提供)
set_unexpected (my_unexpected_func);//设置自己提供的unexpected函数
set_terminate(my_terminate_func);//设置自己提供的erminate函数
"unexpected"函数用来处理声明之外的异常
"terminate"函数用来处理"catch分支未捉到异常"//catch分支未捉到异常最终会调用到terminate函数
也就是说,系统默认处理异常函数,可以由自己设置,但最终还是会调用系统终止函数,但系统终止函数也可以自己设置,但是最终程序还是会崩溃退出。
例:
class MyException
{
public:
virtual void what(void)
{
cout<<"This is MyException"<<endl;
}
};
class MySubException : public MyException
{
public:
void what(void)
{
cout<<"This is MySubException"<<endl;
}
};
void C(int i) throw(int, double)//声明可能扔出哪些异常,这里没包含全
{
int a = 1;
double b= 1.2;
float c = 1.3;
if (i == 0)
{
cout<<"In C, it is OK"<<endl;
}
else if (i == 1)
throw a;
else if (i == 2)
throw b;
else if (i == 3)
throw c;
else if (i == 4)
throw MyException();
else if (i == 5)
throw MySubException();
}
void B(int i)
{
cout<<"call C ..."<<endl;
C(i);
cout<<"After call C"<<endl;
}
void A(int i)
{
try {
B(i);
} catch (int j)
{
cout<<"catch int exception "<<j<<endl;
} catch (double d)
{
cout<<"catch double exception "<<d<<endl;
} catch (MyException &e)//可以处理其子类异常(向上转换,隐式转换)
{
e.what();//由于是虚函数,所以调用的还是派生类的
} catch (...)
{//多种类型的异常,可以有多种捕捉,但是太多异常的话,就麻烦了,此时使用省略号来通配
cout<<"catch other exception "<<endl;
}
}
void my_unexpected_func()//自己提供的unexpected函数
{
cout<<"my_unexpected_func"<<endl;
}
void my_terminate_func ()//自己提供的erminate函数
{
cout<<"my_terminate_func"<<endl;
}
int main(int argc, char **argv)
{
int i;
set_unexpected (my_unexpected_func);//设置自己提供的unexpected函数
set_terminate(my_terminate_func);//设置自己提供的erminate函数
if (argc != 2)
{
cout<<"Usage: "<<endl;
cout<<argv[0]<<" <0|1|2|3>"<<endl;
return -1;
}
i = strtoul(argv[1], NULL, 0);
A(i);
return 0;
}
C++之高级编程20170914的更多相关文章
- 读《C#高级编程》第1章问题
读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...
- MVC高级编程+C#高级编程
本人今年的目标是学习MVC高级编程和C#高级编程,把自己的基础打的扎实,本文中值是一个开到,定期会在上面记录学习的技术点和心得就,加油吧!!!!!
- 《C#高级编程》读书笔记
<C#高级编程>读书笔记 C#类型的取值范围 名称 CTS类型 说明 范围 sbyte System.SByte 8位有符号的整数 -128~127(−27−27~27−127−1) sh ...
- jquery插件开发继承了jQuery高级编程思路
要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而建立起了一个生态系统.这好比大公司们争相做平台一样,得平台者得天下.苹果,微软,谷歌等巨头,都有各自的平台及生态圈 ...
- jQuery高级编程
jquery高级编程1.jquery入门2.Javascript基础3.jQuery核心技术 3.1 jQuery脚本的结构 3.2 非侵扰事JavaScript 3.3 jQuery框架的结构 3. ...
- (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- multiple definition of `err_sys' 《UNIX环境高级编程》
本文地址:http://www.cnblogs.com/yhLinux/p/4079930.html 问题描述: [点击此处直接看解决方案] 在练习<UNIX环境高级编程>APUE程序清单 ...
- Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程
Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程 教程简介: 本教程共71节,主要介绍了shell的相关知识教程,如shell编程需要的基础知识储备.shell脚本概念介 ...
- unix环境高级编程基础知识之第二篇(3)
看了unix环境高级编程第三章,把代码也都自己敲了一遍,另主要讲解了一些IO函数,read/write/fseek/fcntl:这里主要是c函数,比较容易,看多了就熟悉了.对fcntl函数讲解比较到位 ...
随机推荐
- AtCoder Grand Contest 026 D - Histogram Coloring
一列中有两个连续的元素,那么下一列只能选择选择正好相反的填色方案(因为连续的地方填色方案已经确定,其他地方也就确定了) 我们现将高度进行离散化到Has数组中,然后定义dp数组 dp[i][j] 表示前 ...
- 小白初识 - 计数排序(CountingSort)
计数排序,属于桶排序特殊的一种. 当要排序n个数据的时候,如果所处的范围不大,我们可以取其中的最大值K,并将数据分散在K个桶里面, 每个桶里面的数据都是相同的(这样省去了桶内排序的时间),然后顺序取出 ...
- Ubuntu用户设置文件说明
Ubuntu用户设置文件说明 Ubuntu作为Linux的一个发行版本,自然具有Linux系统的多用户特性.因为经常会使用和管理Ubuntu的用户,现将Ubuntu系统下的User的个性化配置整理如下 ...
- 生成dataset的几种方式
1.常用的方式通过sparksession读取外部文件或者数据生成dataset(这里就不讲了) 注: 生成Row对象的方法提一下:RowFactory.create(x,y,z),取Row中的数据 ...
- zabbix_agentd-install.sh (脚本部署zabbix_agentd服务)
原文发表于cu:2016-05-20 基于http://www.cnblogs.com/netonline/p/7406598.html(http://blog.chinaunix.net/uid-2 ...
- VR产业链全景图
- PHP autoload与spl_autoload自动加载机制的深入理解
PHP autoload与spl_autoload自动加载机制的深入理解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-05我要评论 本篇文章是对PHP中的autoload与spl_ ...
- Is It A Tree?(并查集)
Description A tree is a well-known data structure that is either empty (null, void, nothing) or is a ...
- scrum立会报告+燃尽图(第二周第四次)
此作业要求参考: https://edu.cnblogs.com/campus/nenu/2018fall/homework/2249 一.小组介绍 组名:杨老师粉丝群 组长:乔静玉 组员:吴奕瑶.公 ...
- 【转】node.js框架比较
我偶然间看到这篇文章,转这个文章并没有什么含义,仅仅是感觉总结的不错,对于新学node的友友们来说希望这篇文章为大家对 Node.js 后端框架选型带来一些帮助,学习不再迷茫,也是让我有个保存,以后参 ...