[{"data":1,"prerenderedAt":436},["ShallowReactive",2],{"page-\u002Fcpp\u002Ffeatures\u002Ftypedef和using":3},{"id":4,"title":5,"body":6,"description":429,"extension":430,"meta":431,"navigation":54,"path":432,"seo":433,"stem":434,"__hash__":435},"content\u002Fcpp\u002Ffeatures\u002Ftypedef和using.md","Typedef和using",{"type":7,"value":8,"toc":427},"minimark",[9,17,20,84,87,90,101,104,119,122,125,128,139,158,161,170,177,204,207,241,252,312,315,389,392,423],[10,11,12,16],"p",{},[13,14,15],"code",{},"typedef"," 是 C 语言中的“老常客”，也是 C 程序员为了让代码更具可读性而最常用的“救星”之一，但是随着C++对的发展，现在地位主键被更为强大的using关键字取代了。",[10,18,19],{},"纯C语言中，我们经常使用typedef来定义类型，比如你可以在C中，定义一个结构体，一般这样写：",[21,22,27],"pre",{"className":23,"code":24,"language":25,"meta":26,"style":26},"language-cpp shiki shiki-themes github-light github-dark","typedef struct {\n    int x, y;\n} Point;\n\nPoint p1; \u002F\u002F 这样就清爽多了\n\nstruct Point {\n    int x, y;\n};\n","cpp","",[13,28,29,37,43,49,56,62,67,73,78],{"__ignoreMap":26},[30,31,34],"span",{"class":32,"line":33},"line",1,[30,35,36],{},"typedef struct {\n",[30,38,40],{"class":32,"line":39},2,[30,41,42],{},"    int x, y;\n",[30,44,46],{"class":32,"line":45},3,[30,47,48],{},"} Point;\n",[30,50,52],{"class":32,"line":51},4,[30,53,55],{"emptyLinePlaceholder":54},true,"\n",[30,57,59],{"class":32,"line":58},5,[30,60,61],{},"Point p1; \u002F\u002F 这样就清爽多了\n",[30,63,65],{"class":32,"line":64},6,[30,66,55],{"emptyLinePlaceholder":54},[30,68,70],{"class":32,"line":69},7,[30,71,72],{},"struct Point {\n",[30,74,76],{"class":32,"line":75},8,[30,77,42],{},[30,79,81],{"class":32,"line":80},9,[30,82,83],{},"};\n",[10,85,86],{},"下面这种写法需要通过struct:struct Point p1这样写，顺便一提 c++中，struct定义完后直接就是类型名了，不需要typedef也可以直接写Point p1，这个是和C不一样的，需要分辨。",[10,88,89],{},"另一个作用就是优化函数指针，我们都知道原本C语言中的函数指针的原始语法反人类，一般这样写：",[21,91,95],{"className":92,"code":93,"language":94,"meta":26,"style":26},"language-c shiki shiki-themes github-light github-dark","void (*callback)(int, int);\n","c",[13,96,97],{"__ignoreMap":26},[30,98,99],{"class":32,"line":33},[30,100,93],{},[10,102,103],{},"那么我们使用typedef可以优化对应的写法：",[21,105,107],{"className":92,"code":106,"language":94,"meta":26,"style":26},"typedef void (*GLFWkeyfun)(int, int); \u002F\u002F 给这种类型的函数起名叫 GLFWkeyfun\nGLFWkeyfun my_callback; \u002F\u002F 瞬间看懂\n",[13,108,109,114],{"__ignoreMap":26},[30,110,111],{"class":32,"line":33},[30,112,113],{},"typedef void (*GLFWkeyfun)(int, int); \u002F\u002F 给这种类型的函数起名叫 GLFWkeyfun\n",[30,115,116],{"class":32,"line":39},[30,117,118],{},"GLFWkeyfun my_callback; \u002F\u002F 瞬间看懂\n",[10,120,121],{},"函数指针是一个变量，可以存储函数在内存中的起始地址，由于它指向的是内存中的指令（可以直接执行），因此可以直接调用。有个有点像的概念叫做匿名函数，它们两个有很多不同。",[10,123,124],{},"函数指针是一个地址变量，指向一个已经定义好了的有名字的函数，匿名函数定义的时候是没有名字的。函数指针捕获能力有限，只能访问全局变量或者传入的参数，匿名函数则是可以捕获外部作用域的局部变量，如果一个匿名函数没有捕获任何外部变量，那么它可以隐式转换为一个函数指针。",[10,126,127],{},"但是函数指针也是真的丑也非常反直觉，就像凭空生成一个名称，这是因为C语言的typedef采取了模仿变量声明逻辑，只要你能写出一个变量声明，那么前面加上typedef，这个变量名就变成了类型名，如果你定义一个指向缓冲生成函数的变量my_func_ptr:",[21,129,133],{"className":130,"code":131,"language":132,"meta":26,"style":26},"language-C shiki shiki-themes github-light github-dark","void (*my_func_ptr) (GLsizei, GLuint*);\n","C",[13,134,135],{"__ignoreMap":26},[30,136,137],{"class":32,"line":33},[30,138,131],{},[10,140,141,142,145,146,148,149,153,154,157],{},"这里编译器知道my_func_ptr是一个变量名，如果我们在前面加上typedef，编译器就会知道不创建一个叫 ",[13,143,144],{},"GL_GENBUFFERS"," 的变量了，而是把 ",[13,147,144],{}," 定义为一个",[150,151,152],"strong",{},"新的类型名称","，这样就不需要",[150,155,156],{},"前向声明","，因为这一行本身就是定义。",[10,159,160],{},"你可以试试using来替代一下：",[21,162,164],{"className":23,"code":163,"language":25,"meta":26,"style":26},"using GL_GENBUFFERS = void(*) (int, int);\n",[13,165,166],{"__ignoreMap":26},[30,167,168],{"class":32,"line":33},[30,169,163],{},[10,171,172,173,176],{},"注意这里的",[13,174,175],{},"(*)","，它的出现可以有三种情况：",[10,178,179,180,183,184,187,188,191,192,195,196,199,200,203],{},"没有括号的情况下，比如 ",[13,181,182],{},"void *func(int);","  这个就被会解析一个名为func的函数，返回一个\n",[13,185,186],{},"void*","指针，如果有括号，类似于",[13,189,190],{},"void (*func)(int)","，这种情况下会被解析为一个名叫func的指针，指向一个返回void的函数。如果是",[13,193,194],{},"(int*) ptr","，这个叫做显示转换，在C++中，我们倾向于使用",[13,197,198],{},"static_cast\u003CT>","这种方式或者",[13,201,202],{},"reinterpret_cast\u003CT>","这种方式，这种带括号的权利太大，很容易出现bug。",[10,205,206],{},"现在我们说说日常最长使用的两个转换：",[21,208,210],{"className":23,"code":209,"language":25,"meta":26,"style":26},"double pi = 3.14159;\nint rounded_pi = static_cast\u003Cint>(pi); \u002F\u002F 安全地截断\n\n\u002F\u002F 在 OpenCV 中的应用\nvoid* rawData = getSomeData(); \nuchar* pixelData = static_cast\u003Cuchar*>(rawData); \u002F\u002F 明确告诉编译器：我知道这是 uchar 指针\n",[13,211,212,217,222,226,231,236],{"__ignoreMap":26},[30,213,214],{"class":32,"line":33},[30,215,216],{},"double pi = 3.14159;\n",[30,218,219],{"class":32,"line":39},[30,220,221],{},"int rounded_pi = static_cast\u003Cint>(pi); \u002F\u002F 安全地截断\n",[30,223,224],{"class":32,"line":45},[30,225,55],{"emptyLinePlaceholder":54},[30,227,228],{"class":32,"line":51},[30,229,230],{},"\u002F\u002F 在 OpenCV 中的应用\n",[30,232,233],{"class":32,"line":58},[30,234,235],{},"void* rawData = getSomeData(); \n",[30,237,238],{"class":32,"line":64},[30,239,240],{},"uchar* pixelData = static_cast\u003Cuchar*>(rawData); \u002F\u002F 明确告诉编译器：我知道这是 uchar 指针\n",[10,242,243,244,247,248,251],{},"这个适应基础的类型转换，类层次结构中的上行转换：将派生类指针\u002F引用转换为基类，这个是安全的。也可以实现类层次的下行转换：将",[150,245,246],{},"基类转换为派生类","，注意，不进行运行时检查，如果确信这个",[150,249,250],{},"基类指针指的是派生类","，那么可以用它来换取高性能。",[21,253,255],{"className":23,"code":254,"language":25,"meta":26,"style":26},"class Base { virtual void func() {} }; \u002F\u002F 必须有虚函数\nclass Derived : public Base { void extra() {} };\n\nBase* b = new Base();\nDerived* d = dynamic_cast\u003CDerived*>(b); \u002F\u002F 转换失败，b 并不是 Derived 类型\n\nif (d) {\n    d->extra();\n} else {\n    \u002F\u002F 转换失败的处理逻辑，类似于 C# 的 if (d == null)\n}\n",[13,256,257,262,267,271,276,281,285,290,295,300,306],{"__ignoreMap":26},[30,258,259],{"class":32,"line":33},[30,260,261],{},"class Base { virtual void func() {} }; \u002F\u002F 必须有虚函数\n",[30,263,264],{"class":32,"line":39},[30,265,266],{},"class Derived : public Base { void extra() {} };\n",[30,268,269],{"class":32,"line":45},[30,270,55],{"emptyLinePlaceholder":54},[30,272,273],{"class":32,"line":51},[30,274,275],{},"Base* b = new Base();\n",[30,277,278],{"class":32,"line":58},[30,279,280],{},"Derived* d = dynamic_cast\u003CDerived*>(b); \u002F\u002F 转换失败，b 并不是 Derived 类型\n",[30,282,283],{"class":32,"line":64},[30,284,55],{"emptyLinePlaceholder":54},[30,286,287],{"class":32,"line":69},[30,288,289],{},"if (d) {\n",[30,291,292],{"class":32,"line":75},[30,293,294],{},"    d->extra();\n",[30,296,297],{"class":32,"line":80},[30,298,299],{},"} else {\n",[30,301,303],{"class":32,"line":302},10,[30,304,305],{},"    \u002F\u002F 转换失败的处理逻辑，类似于 C# 的 if (d == null)\n",[30,307,309],{"class":32,"line":308},11,[30,310,311],{},"}\n",[10,313,314],{},"这个dynamic_cast近似于c#中的as操作符，专门用于处理多态那中带有virtual函数的类，它会在程序运行时检查对象的真实类型，如果转换是合法的话，就会返回指针，如果失败，就会返回nullptr，它适用于安全的下行转换，不确定基类指针到底指向哪个子类的时候使用。",[21,316,318],{"className":23,"code":317,"language":25,"meta":26,"style":26},"\u002F\u002F 1. 定义一个普通函数\nvoid MyKeyCallback(int key, int action) { \n    \u002F* 处理按键 *\u002F \n}\n\n\u002F\u002F 2. 声明一个函数指针变量\n\u002F\u002F 格式：返回类型 (*指针名)(参数列表)\nvoid (*ptr)(int, int); \n\n\u002F\u002F 3. 赋值（函数名本身就是地址）\nptr = MyKeyCallback;\n\n\u002F\u002F 4. 通过指针调用\nptr(10, 1);\n",[13,319,320,325,330,335,339,343,348,353,358,362,367,372,377,383],{"__ignoreMap":26},[30,321,322],{"class":32,"line":33},[30,323,324],{},"\u002F\u002F 1. 定义一个普通函数\n",[30,326,327],{"class":32,"line":39},[30,328,329],{},"void MyKeyCallback(int key, int action) { \n",[30,331,332],{"class":32,"line":45},[30,333,334],{},"    \u002F* 处理按键 *\u002F \n",[30,336,337],{"class":32,"line":51},[30,338,311],{},[30,340,341],{"class":32,"line":58},[30,342,55],{"emptyLinePlaceholder":54},[30,344,345],{"class":32,"line":64},[30,346,347],{},"\u002F\u002F 2. 声明一个函数指针变量\n",[30,349,350],{"class":32,"line":69},[30,351,352],{},"\u002F\u002F 格式：返回类型 (*指针名)(参数列表)\n",[30,354,355],{"class":32,"line":75},[30,356,357],{},"void (*ptr)(int, int); \n",[30,359,360],{"class":32,"line":80},[30,361,55],{"emptyLinePlaceholder":54},[30,363,364],{"class":32,"line":302},[30,365,366],{},"\u002F\u002F 3. 赋值（函数名本身就是地址）\n",[30,368,369],{"class":32,"line":308},[30,370,371],{},"ptr = MyKeyCallback;\n",[30,373,375],{"class":32,"line":374},12,[30,376,55],{"emptyLinePlaceholder":54},[30,378,380],{"class":32,"line":379},13,[30,381,382],{},"\u002F\u002F 4. 通过指针调用\n",[30,384,386],{"class":32,"line":385},14,[30,387,388],{},"ptr(10, 1);\n",[10,390,391],{},"虽然说typedef在C++中依然有效，但是现代C++程序员更倾向使用using，主要是因为using的可读性更好，遵循从左到右的赋值逻辑。",[21,393,397],{"className":394,"code":395,"language":396,"meta":26,"style":26},"language-c++ shiki shiki-themes github-light github-dark","\u002F\u002F typedef 逻辑是：定义一个变量，前面加个关键字变类型\ntypedef std::vector\u003Cstd::string> StringList;\n\n\u002F\u002F using 逻辑是：这个名字 = 那个类型（像赋值一样直观）\nusing StringList = std::vector\u003Cstd::string>;\n","c++",[13,398,399,404,409,413,418],{"__ignoreMap":26},[30,400,401],{"class":32,"line":33},[30,402,403],{},"\u002F\u002F typedef 逻辑是：定义一个变量，前面加个关键字变类型\n",[30,405,406],{"class":32,"line":39},[30,407,408],{},"typedef std::vector\u003Cstd::string> StringList;\n",[30,410,411],{"class":32,"line":45},[30,412,55],{"emptyLinePlaceholder":54},[30,414,415],{"class":32,"line":51},[30,416,417],{},"\u002F\u002F using 逻辑是：这个名字 = 那个类型（像赋值一样直观）\n",[30,419,420],{"class":32,"line":58},[30,421,422],{},"using StringList = std::vector\u003Cstd::string>;\n",[424,425,426],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":26,"searchDepth":39,"depth":39,"links":428},[],"typedef 是 C 语言中的“老常客”，也是 C 程序员为了让代码更具可读性而最常用的“救星”之一，但是随着C++对的发展，现在地位主键被更为强大的using关键字取代了。","md",{},"\u002Fcpp\u002Ffeatures\u002Ftypedef和using",{"description":429},"cpp\u002Ffeatures\u002Ftypedef和using","OBxn4QRoKwZeLxva1J6M4lJ9VTOK-Kxm7kcfFvVgBEk",1791042462867]