雄安优秀网站建设公司,网站做更改后台怎么做,办公室装修风格效果图,无锡网站开发平台回调 回调函数就是一个被作为参数传递的函数。在C语言中#xff0c;回调函数只能使用函数指针实现#xff0c;在C、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。 回调函数的使用可以大大提升编程的效率#xff0c;这使得它在现代编程中被非常多地使…回调 回调函数就是一个被作为参数传递的函数。在C语言中回调函数只能使用函数指针实现在C、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。 回调函数的使用可以大大提升编程的效率这使得它在现代编程中被非常多地使用。同时有一些需求必须要使用回调函数来实现。 非常知名的回调函数调用有C/C标准库stdlib.h/cstdlib中的快速排序函数qsort和二分查找函数bsearch中都会要求的一个与strcmp类似的参数用于设置数据的比较方法。 反射机制
#include map
#include iostream
#include string
using namespace std;typedef void* (*PTRCreateObject)(void); class ClassFactory {
private: mapstring, PTRCreateObject m_classMap ; ClassFactory(){}; //构造函数私有化public: void* getClassByName(string className); void registClass(string name, PTRCreateObject method) ; static ClassFactory getInstance() ;
};void* ClassFactory::getClassByName(string className){ mapstring, PTRCreateObject::const_iterator iter; iter m_classMap.find(className) ; if ( iter m_classMap.end() ) return NULL ; else return iter-second() ;
} void ClassFactory::registClass(string name, PTRCreateObject method){ m_classMap.insert(pairstring, PTRCreateObject(name, method)) ;
} ClassFactory ClassFactory::getInstance(){static ClassFactory sLo_factory; return sLo_factory ;
} class RegisterAction{
public:RegisterAction(string className,PTRCreateObject ptrCreateFn){ClassFactory::getInstance().registClass(className,ptrCreateFn);}
};#define REGISTER(className) \className* objectCreator##className(){ \return new className; \} \RegisterAction g_creatorRegister##className( \#className,(PTRCreateObject)objectCreator##className)// test class
class TestClass{
public:void m_print(){couthello TestClassendl;};
};
REGISTER(TestClass);int main(int argc,char* argv[]) {TestClass* ptrObj(TestClass*)ClassFactory::getInstance().getClassByName(TestClass);ptrObj-m_print();
}仿函数/函数对象 想象一下这种场景当我们有一个过滤函数Process是用来过滤数组中大于10的元素我们可以为“比较元素是否大于10”这个操作定义成一个函数F作为函数指针传入前面的过滤函数中。 如果此时要求把10也作为参数传入可能就不那么美妙了。这个过滤函数Process可能来自第三方库我们并不能直接修改它的参数定义那我们只好把10这个值定义成一个全局变量再在函数F中调用。 这种做法虽然可行但是我们被迫要维护一个全局变量容易出错。比如有一个同名的全局变量那就会导致命名空间污染。总之更优雅的方式就应运而生了它就是仿函数。 仿函数functor又称函数对象是指实现一个简单的类并且重载了operator()操作符该类的实例化对象就可以行使函数的功能了。 以上例子我们就可以定义个仿函数在构造函数中将10作为参数传入函数对象内由函数对象内部进行维护。同时重载一个operator()操作符在函数体中使用这个10即可。 仿函数的优点是能做到封装功能扩展起来非常方便而回调函数则没有办法。所以在STL的实现中运用了大量仿函数来实现谓词。 //一元谓词——回调函数
void showFunc(int item) {std::cout 调用回调函数 item std::endl;
}//一元谓词——仿函数
class showFuncObj {
public:showFuncObj() : m_count(0) {}void operator()(int item) {std::cout 调用仿函数 item std::endl;m_count;}void count() { return m_count; }
private:m_count;
};int main() {std::vectorint vec {1,2,3,4,5,6,7};for_each(vec.begin(), vec.end(), showFunc);showFuncObj s;s for_each(vec.begin(), vec.end(), s); //仿函数是按值传参s.count();return 0;
}std::for_each()
C STL中 for_each 算法存在的意义是什么 - 知乎
函数接受三个参数给定范围的起始迭代器、终止迭代器和一个可调用对象。它通过循环遍历范围内的每个元素并将该元素传递给可调用对象进行处理。
std::for_each 相较于for而言它可以帮助在STL容器中的每个元素都执行一次fn()函数
C11
range-based for loops
C11 新特性之Range-based for loops_51CTO博客_C11新特性
vectorint vec;
vec.push_back( 10 );
vec.push_back( 20 );for (int i : vec )
{cout i;
}
vector vv{1,2,3,4,5,6,7,8,9,10};for_each(vv.begin(), vv.end(), [](auto item){ // 可以但没必要fmt::print({}, item);});for (auto itvv.begin(); it!vv.end(); it) { // 不推荐fmt::print({}, *it);}for (auto item : vv) { // 推荐fmt::print({}, item);}
std::function 函数包装器类模版std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::function对象是对C中现有的可调用实体的一种类型安全的包裹我们知道像函数指针这类可调用实体是类型不安全的。 std::function可以取代函数指针的作用因为它可以延迟函数的执行特别适合作为回调函数使用。它比普通函数指针更加的灵活和便利。 std::function可以说是函数指针的超集它除了可以指向全局和静态函数还可以指向彷函数lambda表达式类成员函数甚至函数签名不一致的函数可以说几乎所有可以调用的对象都可以当做std::function当然对于后两个需要使用std::bind进行配合 std::function可以指向类成员函数和函数签名不一样的函数其实这两种函数都是一样的因为类成员函数都有一个默认的参数this作为第一个参数这就导致了类成员函数不能直接赋值给std::function这时候我们就需要std::bind了简言之std::bind的作用就是转换函数签名将缺少的参数补上将多了的参数去掉甚至还可以交换原来函数参数的位置
一旦bind补充了缺失的参数那么以后每次调用这个function时那些原本缺失的参数都是一样的举个栗子上面代码中callback6我们每次调用它的时候第二个参数都只会是100。正因为第一点所以假如我们是在iOS程序中使用std::bind传入一个缺失参数那么我们转化后的那个function会持有那些缺失参数这里我们需要防止出现循环引用导致内存泄漏。std::function与std::bind使用总结-腾讯云开发者社区-腾讯云 任何东西都会有优缺点std::function填补了函数指针的灵活性但会对调用性能有一定损耗经测试发现在调用次数达10亿次时函数指针比直接调用要慢2秒左右而std::function要比函数指针慢2秒左右这么少的损耗如果是对于调用次数并不高的函数替换成std::function绝对是划得来的。 //包装仿函数
class Functor {
public:int operator()(int a) { return a; }
};int main() {auto f std::functionint(int)(Functor{});std::cout f(1) f(1) std::endl;return 0;
}std::bind
是一个函数适配器接受一个可调用对象callable object生成一个新的可调用对象来“适应”原对象的参数列表。
bind接收的第一个参数必须是一个可调用的对象f包括函数、函数指针、函数对象、和成员函数指针之后bind最多接受9个参数参数数量必须与f的参数数量相等这些参数被传递给f作为入参。 绑定完成后bind会返回一个函数对象它内部保存了f的拷贝具有operator()返回值类型被自动推导为f的返回类型。在发生调用时这个函数对象将把之前存储的参数转发给f完成调用。
std::bind将可调用对象与其参数一起进行绑定绑定后的结果可以使用std::function保存。std::bind主要有以下两个作用
将可调用对象和其参数绑定成一个彷函数只绑定部分参数减少可调用对象传入的参数。
闭包
闭包Closure可以被理解为一个附带数据的操作
闭包closure一般是指带有状态的函数这里的状态指的是调用环境的上下文。一个函数带上了状态就是闭包。那么闭包就需要有捕获并持有外部作用域变量的能力闭包状态的捆绑发生在运行时。在C中闭包的实现方式包括仿函数、std::bind()绑定器以及lambda表达式。
仿函数的必包 C 98 中并没有严格意义上的闭包但我们可以用仿函数Functor来模拟闭包的行为仿函数即一个重载了小括号操作符的类这个类拥有与函数相近的行为方式它拥有自己的私有成员变量
传统的实现闭包的方式但是并不方便并不能隐式捕获全体外部作用域变量需要每个变量都对应构造函数的参数而且每一段闭包代码都要单独定义一个仿函数的类。
std::bind()绑定器
ambda表达式
lambda并不是完美的闭包但是它提供了多种捕获方式完美结合了C的语言特性。因为捕获变量并不能真正延长变量的生命周期它只是提供了复制和引用的捕获语义。因为语言特性的关系(RAII)C无法延长变量作用域因为局部变量永远都需要在作用域结束后析构
匿名函数 lambda表达式是C11引入的新特性在编译器层面本质是一种仿函数。值传递捕获的参数转成对应类型的成员变量引用传递捕获的参数转换成对应类型指针的成员变量。 捕获可以分为按值捕获和按引用捕获。非局部变量如静态变量、全局变量等可以不经捕获直接使用 lamdba表达式产生的是函数对象/仿函数把匿名函数看作一个类对象就行上面就很好理解啦 类似参数传递变量的捕获方式可以是值或引用。与传值参数类似采用值捕获的前提是变量可以拷贝。被捕获的变量的值是在lambda创建时拷贝而不是调用时拷。由于被捕获变量的值是在lambda创建时拷贝因此随后对其修改不会影响到lambda内对应的值 外部变量指的是和当前 lambda 表达式位于同一作用域内的所有局部变量 全局部变量无论为什么访问方式都可以对其进行修改 引用如果外部变量的访问方式为引用则可以在函数内部使用修改外部变量 传值如若外部变量的访问方式为传值则只能在函数内使用变量不能修改外部变量要想修改外部变量则可借助mutable关键字但是修改的仅是拷贝真正的外部变量不会变化 int main()
{int var 5;static int base 3;functionint() f [] () mutable - int {var;base;return var base;};auto ret1 f();auto ret2 f();cout ret1 ret1 ret2 ret2 endl var var base base endl;return 0;
}//结果
//ret1 10 ret2 12*importent*
//var 5 base 5 mutable关键字的作用对 [] 捕获的变量进行去const操作但是不会影响外部变量 1. lambda匿名函数捕获变量的时机在第一次定义的时候而不是多次调用的时候。调用两次f(), 并不会捕获两次外部变量 2. lambda匿名函数其实是一个函数对象所以lambda定义时按值捕获的时候变量已经存在与这个lambda函数对象里面了因此后续多次调用该lambda匿名函数的时候都是修改的对象里面的值。 左值右值
万能引用与完美转发-CSDN博客
#includeiostreamusing std::cout; using std::endl;void Fun(int x) { cout 左值引用 endl; }
void Fun(const int x) { cout const 左值引用 endl; }void Fun(int x) { cout 右值引用 endl; }
void Fun(const int x) { cout const 右值引用 endl; }templateclass T
void Perfect(T t)
{Fun(t);//会一直调用左值Fun(std::forwardT(t));
}int main()
{Perfect(1);int a 5;Perfect(a);Perfect(std::move(a));const int b 10;Perfect(b);Perfect(std::move(b));return 0;
}模板中的不代表右值引用而是万能引用其既能接收左值又能接收右值。 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力。
只要右值传参就一定要使用完美转发
std::forward传入的左值传递给调用函数仍然为左值传入的右值传递给调用函数的仍为右值不会退化为左值。 模版
编译器并不是把函数模板处理成能够处理任意类的函数编译器从函数模板通过具体类型产生不同的函数编译器会对函数模板进行两次编译在声明的地方对模板代码本身进行编译在调用的地方对参数替换后的代码进行编译。 可变参数模板
C11可变参数 - 简书
可变模版参数函数
可变模版参数类
在C11之前类模板和函数模板只能含有固定数量的模板参数。C11增强了模板功能允许模板定义中包含0到任意个模板参数这就是可变参数模板。可变参数模板的加入使得C11的功能变得更加强大而由此也带来了许多神奇的用法。
变参数模板顾名思义就是参数个数和类型都可能发生变化的模板要实现这一点那就必须要使用模板形参包。
template class ...Args void Function(Args... args){}
Args是模板参数包。 args函数形参参数包 声明Args… args这个参数包中可能有0-任意个参数
方法一递归展开参数包的方法如下
#includeiostreamusing std::cout; using std::endl;void FunctionArg() { cout endl; }template class T,class ...Args
void FunctionArg(T value,Args... args)
{cout 参数个数为 sizeof...(args) endl;cout value endl;FunctionArg(args...);
}template class ...Args
void Function(Args... args)
{FunctionArg(args...);
}int main()
{Function(std::string(sort), 1, A);Function(1, 3, 2.34);
}模板可变参数并不支持Args[i]来找参数 同时模板在展开参数时也不支持if语句。 因为if与else是逻辑代码编译器在展开参数包的时是编译过程模板在推演类型。同理也可以解释Args[i]型找参数
可变参数模板在实际的使用中更多还是结合完美转发来使用实现对象的统一构造或者接口调用封装等。可变参数的存在使得模板接口的灵活度提升了一个档次
templatetypename T, typename... Args std::unique_ptrT make_unique(Args... args) { return std::unique_ptrT(new T(std::forwardArgs(args)...)); }
RAII (Resource Acquisition Is Initialization) RAIIResource Acquisition Is Initialization,也称为“资源获取就是初始化”是C语言的一种管理资源、避免泄漏的惯用法。C标准保证任何情况下已构造的对象最终会销毁即它的析构函数最终会被调用。简单的说RAII 的做法是使用一个对象在其构造时获取资源在对象生命期控制对资源的访问使之始终保持有效最后在对象析构的时候释放资源。 RAII是C的发明者Bjarne Stroustrup提出的概念“资源获取即初始化”也就是说在构造函数中申请分配资源在析构函数中释放资源。因为C的语言机制保证了当一个对象创建的时候自动调用构造函数当对象超出作用域的时候会自动调用析构函数。所以在RAII的指导下我们应该使用类来管理资源将资源和对象的生命周期绑定。 简单而言RAII就是指资源在我们拿到时就已经初始化一旦不在需要该资源就可以自动释放该资源。 对于C来说资源在构造函数中初始化可以再构造函数中调用单独的初始化函数在析构函数中释放或清理。常见的情形就是在函数调用中创建C对象时分配资源在C对象出了作用域时将其自动清理和释放不管这个对象是如何出作用域的不管是否因为某个中间步骤不满足条件而导致提前返回也不管是否正常走完全部流程后返回。
RAII 要求资源的有效期与持有资源的对象的生命期严格绑定即由对象的构造函数完成资源的分配获取同时由析构函数完成资源的释放。在这种要求下只要对象能正确地析构就不会出现资源泄露问题。
decltype的作用
https://www.cnblogs.com/soloveu/p/14607370.html
泛型编程中结合auto用于追踪函数的返回值类型函数写为尾置返回类型. 这也是decltype最大的用途了。
template typename _Tx, typename _Ty
auto multiply(_Tx x, _Ty y)-decltype(_Tx*_Ty)
{return x*y;
}
NULL与nullptr区别
用nullptr
通信协议
mcpackpb
QT
QT信号与槽。
Qt原理-窥探信号槽的实现细节 - 知乎
QT事件
QT-事件循环机制 - 掘金
【精选】Qt QEvent 介绍-CSDN博客