Day16(上).函数指针//socket动态库回调函数开发//泛型编程//类模板
来源:互联网 发布:js添加class类 编辑:程序博客网 时间:2024/05/22 18:55
老习惯,回忆一下:
多态
重写 PK 重载理解
函数重载
必须在同一个类中进行
子类无法重载父类的函数,父类同名函数将被名称覆盖
重载是在编译期间根据参数类型和个数决定函数调用
函数重写
必须发生于父类与子类之间
并且父类与子类中的函数必须有完全相同的原型
使用virtual声明之后能够产生多态(如果不使用virtual,那叫重定义)
多态是在运行期间根据具体对象的类型决定函数调用
请谈谈你对多态的理解
多态的实现效果
多态:同样的调用语句有多种不同的表现形态;
多态实现的三个条件
有继承、有virtual重写、有父类指针(引用)指向子类对象。
多态的C++实现
virtual关键字,告诉编译器这个函数要支持多态;不要根据指针类型判断如何调用;而是要根据指针所指向的实际对象类型来判断如何调用
多态的理论基础
动态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。
多态的重要意义
设计模式的基础。
实现多态的理论基础
函数指针做函数参数
多态原理探究
//分析一下要想实现多态,c++编译器应该动什么手脚
//第一个需要动手脚的地方 起码这个函数print 我应该特殊处理
virtual void print()
{
cout<<"a"<<a<<endl;
}
//父类虚函数有virtual关键字,子类无论有没有关键字,都默认添加virtual关键字
//pBase 我怎么知道是父类对象还是子类对象
//动手脚2::区分是父类对象还是子类对象,提前布局
pBase->print(); //
虚函数表指针(VPTR)被编译器初始化的过程
基类和子类对象指针++混搭风
/指针也是一种数据类型,指针数据的数据类型是指,它所指的内存空间的数据类型//最后一点引申 指针的步长 。。。c++class Parent01{protected:int i;intj;public:virtual void f(){cout<<"Parent01::f"<<endl;}};class Child01 : public Parent01{public:int k;public:Child01(int i, int j){printf("Child01:...do\n");}virtual void f(){printf("Child01::f()...do\n");}};void howToF(Parent01 *pBase){pBase->f();}//指针的步长 在c++领域仍然有效,父类指针的步长和子类指针的步长不一样//多态是靠迟绑定实现的(vptr+函数指针实现)int main06(){int i = 0;Parent01* p = NULL;Child01* c = NULL;//不要把父类对象还有子类对象同事放在一个数组里面Child01 ca[3] = {Child01(1, 2), Child01(3, 4), Child01(5, 6)};//不要用父类指针做赋值指针变量,去遍历一个子类的数组。//把数组的首地址,赋给基类指针p = ca;//把数组的首地址,赋给子类指针c = ca;p->f();c->f(); //有多态发生// p++;// c++;// // p->f();//有多态发生// c->f();for (i=0; i<3; i++){howToF(&(ca[i]));}system("pause");return 0;}
为什么要定义虚析构函数(非常重要)
#include "iostream"using namespace std;class AA{public:AA(int a= 0){this->a = a;print(); //在构造函数里面能实现多态吗?}virtual ~AA(){cout<<"父类析构函数do"<<endl;}//分析一下要想实现多态,c++编译器应该动什么手脚//第一个需要动手脚的地方 起码这个函数print 我应该特殊处理virtual void print(){cout<<"父类的"<<"a"<<a<<endl;}protected:int a ;};class BB : public AA{public:BB(int a= 0, int b = 0){this->a = a;this->b = b;}~BB(){cout<<"子类析构函数do"<<endl;}virtual void print(){cout<<"子类的"<<"a"<<a<<"b"<<b<<endl;}private:int b ;};//如果想通过父类指针 执行 所有的子类对象的析构函数,那么需要在父类析构函数前加上virtual关键字//把父类的析构函数变成虚析构函数 void howToDelete(AA *pBase){delete pBase;}void main(){BB *b1 = new BB(1, 2);b1->print();howToDelete(b1);//子类对象的时候,//delete b1;system("pause");}纯虚函数和接口类的基础
具体代码参看这节最后:http://blog.csdn.net/jorg_zhao/article/details/46741515
整个工程的压缩包下载地址:http://pan.baidu.com/s/1eQCSmQ2
C++中类封装了成员函数,C中函数有handle
********************************************************************************************************************
回调函数
先回忆一下C语言中的知识点:
1. 函数类型基础
函数的三要素:名称、参数、返回值
C语言中通过typedef为函数类型重命名
语法:
typedef type name(parameter list);typedef int func(int, int);typedef void func(int);
代码实例:
#include "stdio.h"#include "stdlib.h"#include "string.h"int arr1[10];//定义一个数组类型typedef int array[10];//定义一个指向数组的数组指针类型typedef int(*parray)[10];void main(){ //由数组类型定义变量 array arr2; //相当于int arr2[10]; arr2[0] = 1; //用数组指针类型去定义一个数组 parray parr3; parr3 = &arr2; (*parr3)[0] =2; //直接定义一个指向数组类型的指针 int(*myparray)[10] = &arr2; (*myparray)[0] = 3; system("pause");}函数指针:
函数指针用于指向一个函数,函数名是执行函数体的入口地址;
1) 可用过函数类型定义函数指针 FuncType* pointer;
2) 也可直接定义:typedef (*pointer) (parameter list);
pointer为函数指针变量名
type为指向函数的返回值类型
parameter list为指向函数的参数类型列表
(1)
#include "stdio.h"#include "stdlib.h"#include "string.h"//1. 定义一个函数类型(然后用类型去指针)typedef int FUNC(int);//函数名称就代表函数的入口地址 函数名称本身就是一个地址int test(int i){return i*i;}int main(){//用函数类型定义一个函数指针FUNC *myfunc = NULL;//通过函数指针(函数的入口地址)可以指向函数体(言外之意就是可以进行函数调用)myfunc = test;printf("%d \n", myfunc(2));//对函数名取多少地址 都是一样的myfunc = &test;printf("%d \n", myfunc(2));system("pause");}上面的代码总结:通过函数指针可以执行一个函数调用
(2)
#include "stdio.h"#include "stdlib.h"#include "string.h"//2. 定义一个指向函数类型的指针类型typedef int(*myfunc) (int);myfunc aa;
(3)
#include "stdio.h"#include "stdlib.h"#include "string.h"void func(){ printf("执行了f\n");}void main(){ //3. 直接定义函数指针,并且赋值 void(*myfunc1)() = func; void(*myfunc2)() = &func; myfunc1(); myfunc2(); system("pause");}
2. 函数指针做函数参数
指针做函数参数 PK 函数指针做函数参数
1. 一级指针做函数参数,二级指针做函数参数, 三级指针。。。。。。
2. 函数指针做函数参数
当函数指针做函数的参数,传递给一个被调用函数,被调用函数丸可以通过这个指针调用外部的函数,这就形成了回调。
#include "stdio.h"#include "stdlib.h"#include "string.h"int add(int a, int b);//第二个函数是函数指针做函数参数//在libfun被调用函数里面,就可以通过这个函数指针调用外部的函数,形成一个回调函数int libfun(int(*pDis)(int a, int b));int add(int a, int b){return a + b;}//pDis是add的入口地址int libfun(int(*pDis)(int a, int b)){int a, b;a = 1;b = 2;//执行了add函数调用(被调用函数调用了外部函数)printf("%d\n", pDis(a, b));return 0;}int main(void){//直接定义了一个函数指针int (*pfunc)(int a, int b);//函数名赋给函数指针,就是函数的入口地址赋给了pfuncpfunc = add;libfun(pfunc);system("pause");}上面的几个函数都是在同一个文件当中
假如
int libfun(int(*pDis)(int a, int b))是一个库中的文件,就只有使用回调了,通过函数指针参数将外部函数地址传入来实现调用
函数add的代码做了修改,也不必改动库的代码,就可以正常实现调用,便于程序的维护和升级。
回调函数的基础知识
回调函数是利用函数指针实现的一种调用机制
回调机制原理
当具体事件发生时,调用者通过函数指针调用具体函数
回调机制的将调用者和被调函数分开,两者互不依赖
1. 需要把待回调函数的入口地址注入到动态库中
2. 动态库业务逻辑函数去调用
********************************************************************************************************************
模版
类属--------类型参数化,又称参数模版
使得程序(算法)可以从逻辑功能上抽象,把被处理的对象(数据)类型作为参数传递
C++提供两种模板机制:函数模版 和 类模板
语法形式: template <类型形式参数表>
代码:
#include "iostream"using namespace std;void swap(int &a, int &b){int c;c = a;a = b;b = c;}void swap(float &a, float &b){float c;c = a;a = b;b = c;}//template关键字告诉C++编译器,现在开始泛型编程//typename告诉C++编译器,T为类型,不要乱报错template <typename T>void T_swap(T &a, T &b){T c;c = a;a = b;b = c;}void main(){int a = 1, b = 2;swap(a, b);float a1 = 1, b1 = 2;swap(a1, b1);//泛型编程的调用方式有两种//1. 自动类型推导int aa = 3, bb = 4;cout << "aa:" << aa << " " << "bb:" << bb << endl;T_swap(aa, bb);cout << "aa:" << aa << " " << "bb:" << bb << endl;//2. 具体类型调用float xx = 0.3, yy = 0.5;cout << "xx:" << xx << " " << "yy:" << yy << endl;T_swap<float>(xx, yy);cout << "xx:" << xx << " " << "yy:" << yy << endl;system("pause");}继续强化:
我们要实现排序算法:
1.普通的做法是这样的:
#include "iostream"using namespace std;int sortArray(int *a, int num){int i,j;for (i = 0; i < num; i++){for (j = 0; j < num; j++){if (a[i] < a[j]){int tmp = a[i];a[i] = a[j];a[j] = tmp;}}}return 0;}void print(int *a, int num){for (int i = 0; i < num; i++){cout << a[i] << endl;}}void main(){int a[10] = { 1, 12, 32, 24, 15, 26, 7, 85, 3, 2 };int num = sizeof(a) / sizeof(*a);sortArray(a, num);print(a, num);system("pause");}
用模版做是这样的:
#include "iostream"using namespace std;template<typename T>int T_sortArray(T *a, int num){int i, j;for (i = 0; i < num; i++){for (j = 0; j < num; j++){if (a[i] < a[j]){T tmp = a[i];a[i] = a[j];a[j] = tmp;}}}return 0;}template<typename T>void T_print(T *a, int num){for (int i = 0; i < num; i++){cout << a[i]<<",";}cout<< endl;}void main(){int a[10] = { 1, 12, 32, 24, 15, 26, 7, 85, 3, 2 };int num = sizeof(a) / sizeof(*a);T_sortArray<int>(a, num);T_print<int>(a, num);char buf[] = "jdl2njvk239vwfl2";int len = strlen(buf);T_sortArray<char>(buf, len);T_print<char>(buf, len);system("pause");}
继续加强:--------函数模版遇上重载
但是太复杂,仅供参考,下面总结一下:
1. 函数模版可以像普通函数一样被重载
2. C++编译器优先考虑普通函数
3. 如果函数模版可以产生一个更好的匹配,那么选择模版
4. 可以通过空模版实参列表的语法限定编译器只通过模版匹配
#include "iostream"using namespace std;int max(int a, int b){ cout << "int max(a,b)" << endl; return a > b ? a : b;}template <typename T>T max(T a, T b){ cout << "T max(a,b)" << endl; return a > b ? a : b;}template <typename T>T max(T a, T b, T c){ cout << "T max(a, b, c)" << endl; return max(max(a, b), c);}void main(){ int a = 1, b = 3; //C++编译器会默认调用具体类型 max(a, b); //强制使用模版 max<>(a, b); int aa = 0.2, bb = 0.5, cc = 0.7; max(aa, bb); max(aa, bb, cc); system("pause");}编译器的深入理解:
1. 编译器并不是把函数模版处理成能够任意类型的函数
2. 编译器从函数模版通过具体类型产生不同的函数
3. 编译器会对函数模版进行两次编译
4. 在声明的地方对模版代码本身进行编译
5. 在调用的地方对参数替换后的代码进行编译
类模板
#include "iostream"using namespace std;template <typename T>class A{public:void setA(T a){this->a = a;}void getA(){return this->a;}private:T a;};void main(){//要把类模板具体成类型后,才能定义变量//A a; //ERRORA<int> a;system("pause");}
#include "iostream"using namespace std;template <typename T>class A{public:A(T a){this->a = a;}void setA(T a){this->a = a;}void getA(){return this->a;}private:T a;};class B : public A<int>{public:B(int a, int b) :A<int>(a){this -> b = b;}private:int b;};void main(){//要把类模板具体成类型后,才能定义变量//A a; //ERRORA<int> a;B b(1,2);system("pause");}
类模版遇上友元函数
................................................
类模板与static成员
从类模型实例化的每个模版类有自己的类模板数据成员,该模版的所有对象共享一个static数据成员
和非模版类的static数据成员一样,模版类的static数据成员也应该在文件范围定义和初始化
每个模版类有自己的类模板的static数据成员副本
- Day16(上).函数指针//socket动态库回调函数开发//泛型编程//类模板
- 类函数指针的动态映射(回调函数)
- 函数型指针与回调函数
- 回调函数 函数指针
- 函数指针回调函数
- 函数指针回调函数
- 函数指针 回调函数
- 回调函数,函数指针
- 在模板类成员函数入参中使用回调函数指针
- 指针函数 、函数指针 、 回调函数
- 指针函数、函数指针、回调函数
- 函数指针和回调函数浅学
- 函数指针和回调函数
- 函数指针及回调函数
- 函数指针与回调函数
- 回调函数和函数指针
- 函数指针和回调函数
- 回调函数:函数指针(一)
- error C2275: 'FILE' : illegal use of this type as an expression
- const_cast<type_id> (expression)
- hdu 4857/BestCoder Round#1 1001(拓扑排序+逆向建图)
- LINUX下的图形系统的一些资料
- js中获取URL参数的方法
- Day16(上).函数指针//socket动态库回调函数开发//泛型编程//类模板
- [转载]C#特性详解
- 值得推荐的C/C++框架和库
- 源代码管理工具——VSS详解
- 按钮点击状态
- ION基本概念介绍和原理分析
- UITextView的内容长度限制
- 欢迎使用CSDN-markdown编辑器
- 有道云笔记网络错误