当前位置: 首页 > wzjs >正文

用html5做课程教学网站平面设计正规培训机构

用html5做课程教学网站,平面设计正规培训机构,长沙百度租车有限公司,望野草莓author: hjjdebug date: 2025年 05月 16日 星期五 15:06:00 CST description: 类模板的简单实例 文章目录 1.实例代码:2. 模板类写法2.1 模板类的构造函数.2.2 模板类中的语句 3. 模板类的实例化过程.3.1 实例化的进一步试验. 4. 怎样调试constexpr 修饰的函数? 类模…

author: hjjdebug
date: 2025年 05月 16日 星期五 15:06:00 CST
description: 类模板的简单实例


文章目录

  • 1.实例代码:
  • 2. 模板类写法
    • 2.1 模板类的构造函数.
    • 2.2 模板类中的语句
  • 3. 模板类的实例化过程.
    • 3.1 实例化的进一步试验.
  • 4. 怎样调试constexpr 修饰的函数?

类模板的概念可能很复杂,假定你已经理解了一些基本概念.
这里用简单实例来巩固一些基本概念.

1.实例代码:

$ cat main.cpp
#include <stdio.h>
//定义一个类模板,带类型参数和非类型参数
//将来可以生成很多类
template <typename Func, int N>
struct ConstArray 
{constexpr ConstArray(Func f) : data{} {for (int i = 0; i < N; ++i)data[i] = f(i);}int data[N];
};
//想调试,你可以去掉constexpr, 编译出debug版本调试
constexpr int myFunc(int i) { return i * 2; }/* 定义一个类模板实例, 实例化类模板, 型实结合的过程* 模板参数1,类型 ,传递实参deltype(&myFunc)* 模板参数2,整数 ,传递整数3** 定义类的对象名称arr,传递对象的构造参数&myFunc*/
//constexpr ConstArray<decltype(&myFunc),5> arr(&myFunc);  // 放开该语句,编译时就能生成数组 {0, 2, 4, 6,8}:int main()
{int(*pTest1)(int) = myFunc;int(*pTest2)(int) = &myFunc;printf("pTest1:%p,pTest2:%p\n",pTest1,pTest2);//两种赋值都能工作,随便选一种吧,都各有各的道理,其实是一样的,选第二种吧.ConstArray<decltype(myFunc),5> arr(myFunc);  //该对象执行时会被直接付给初值,数值是编译期调用构造计算好的.ConstArray<decltype(&myFunc),5> arr2(&myFunc); return 0;
}

运行:
./tt
pTest1:0x401136,pTest2:0x401136

2. 模板类写法

template <typename Func, int N>
struct ConstArray

template 是关键字, 表示模板, 模板就是模子, 就是由它可造出一堆东西来.
typename 是关键字, 表示模板类型, 也可以用class 关键字替换.
Func 是模板类型名称,是一个标识符, 你可以改成你喜欢的名字. 它代表了可变的类型.
int 是关键字, 是确定的类型为int
N 是标识符,可改为你喜欢的名字. 是非类型参数, 将来由固定的数值替换.
struct 是关键字, 代表定义的是类.
ConstArray 是标识符, 代表类模板的名称
与普通类定义比较,多了模板参数声明部分,这正式模板类的精华所在, 类型是不固定的,由参数确定类型.
模板类只注重算法,不注重类型,进一步把算法分离.
模板参数还可以传递固定类型的参数,例如这里的N,它是一个整形数, 叫非类型模板参数. 通常用来处理编译期常量

如果按定义读下来, 似乎模板类更自然, 但从意义而言,类模板更贴切,
类模板,模板类含义差不多,也就混用了.

2.1 模板类的构造函数.

constexpr ConstArray(Func f) : data{}

constexpr 是关键字, 表示函数可以在编译器调用.
ConstArray 与类同名,表示是构成函数
Func 是模板类传来的类型
f 是类型的实例,是对象, 其名称是标识符,可以随便起名.
data 是该类定义的 int 型数据成员. 是一个数组名称,数组大小为模板参数N

2.2 模板类中的语句

分析一条函数语句:
for (int i = 0; i < N; ++i) data[i] = f(i);
for循环意义比较明确,仅有f(i) 看着别扭, f 是我们传来的类型的实例, f(i)是什么意思?
如果f是一个函数指针,那f(i) 就是函数调用. 所以在实例化时要传递函数地址.

3. 模板类的实例化过程.

constexpr ConstArray<decltype(&myFunc),5> arr(&myFunc);

/* 定义一个类模板实例, 实现型实结合的过程

  • 模板参数1,类型 ,类型是gcc 从传递的构造参数推导出来的deltype(&myFunc)
  • 模板参数2,整数 ,传递整数5 , 表示只运算5次
  • 定义类的对象名称arr,传递对象的构造参数&myFunc

我们看看gdb中打印的arr, 这就是gcc 我们编译的计算机认识的对象. 一目了然.

(gdb) p arr
$3 = {data = {0, 2, 4, 6, 8}
}
(gdb) ptype arr
type = const struct ConstArray<int (*)(int), 5> [with Func = int (*)(int)] {int data[5];public:ConstArray(Func);
}

类型的名称就有点长了,是模板类型 struct ConstArray<int (*)int,5> 类型

3.1 实例化的进一步试验.

我们把实例化改一改,如下:
constexpr ConstArray<decltype(&myFunc),5> arr(1);
编译出错:
error: invalid conversion from ‘int’ to ‘int ()(int)’ [-fpermissive]
22 | constexpr ConstArray<decltype(&myFunc),5> arr(1); // 编译时生成数组 {0, 2, 4, 6,8}:
| ^
| |
| int
咿! 它怎么知道我传1不对,要求传int(
)(int) 函数呢? 噢! 是 decltype(&myFunc)推导出来的.
重新实例化:
constexpr ConstArray<int,5> arr(1);
编译报错:
main.cpp:9:42: error: ‘f’ cannot be used as a function
9 | for (int i = 0; i < N; ++i) data[i] = f(i);
| ^~
gcc 抱怨我们传入的类型对象没法当函数来调用, 是的,我们传入的是整数1,当然不能当函数.
这样对传递的类型要求有了更清晰的认识.
可见模板类会对传递的类型进行检查, 对i语句语法进行检查, 如果gcc不理解,就会报错,
可见其使用还是很安全的. 是啊,生不成执行代码,gcc 肯定是不干的.

如果我不用编译期生成数据,而在运行期再生成数据,是怎样的过程呢?
反编译发现,竟是直接把数值付给类成员, 调用构造函数及回调函数根本就没有执行.
26 ConstArray<decltype(&myFunc),5> arr(&myFunc);
0x0000000000401151 <+27>: movl $0x0,-0x20(%rbp)
0x0000000000401158 <+34>: movl $0x2,-0x1c(%rbp)
0x000000000040115f <+41>: movl $0x4,-0x18(%rbp)
0x0000000000401166 <+48>: movl $0x6,-0x14(%rbp)
0x000000000040116d <+55>: movl $0x8,-0x10(%rbp)

可见gcc 在编译期,根据用户提供的线索,构造函数,回调函数,按解释性语句已经帮我们计算好了数据,
直接来构建了对象, gcc 很强大!

4. 怎样调试constexpr 修饰的函数?

此例我想看看构造过程,因为如果函数比较复杂,运行出错怎么办?
你只需要去掉constexpr 就可以了. debug版还可以跟入函数内部.
否则的话, 静态编译,你只能看到编译的结果而看不到具体的过程.
调试的时候,myFunc 的地址也可以看见了.
(gdb) p myFunc
$6 = {int (int)} 0x401136 <myFunc(int)>
它说myFunc 地址是0x401136
类型: 输入参数是int,输出参数是int的函数类型

(gdb) p &myFunc
函数名称本来就是地址0x401136, 函数名称再取地址是什么意思?
由调试知道, 它认为还是地址,地址没有变,但类型变了,是函数指针类型的地址,
$7 = (int (*)(int)) 0x401136 <myFunc(int)>

这与我们通常意义上的变量取地址是不一样的内涵!
变量取地址是变量所处的内存地址. 函数名称取地址其值还是函数的地址,这只能算是和gcc的约定了.

但这就要小心了,函数指针解引用,其值还是函数地址.
即从0x401136函数指针解引用还是得0x401136函数地址, 效果上好象调不调解引用都一样

(gdb) ptype myFunc
type = int (int)
(gdb) ptype &myFunc
type = int (*)(int)

int(int) 与 int(*) int 有什么区别呢?

前者为函数类型,后者为函数指针类型.如果一定要区分的话.
我们定义一个函数指针pTest,为其赋值即可调用函数.
int (*pTest1) (int) = myFunc;
int (*pTest2) (int) = &myFunc;
两种赋值方式都是可以通过编译的,且pTest1 完全等价于pTest2
c++中允许写为 pTest(5); 这种形式来调用函数

实例化类时我传myFunc,或传&myFunc 结果都是对的, 因为它们传的都是0x401136
而且其构造函数的参数类型f 都是
(gdb) p f

4 = (int (*)(int)) 0x401136 <myFunc(int)>

在这里得到了统一.

int (int) 类型的Func类型声明一个变量f, 居然是int (*) (int) 类型的, 看起来还是有一点点不爽,

所以正宗的还是传递&myFunc 吧. 这样它们的类型一致性更好.
而且了解到myFunc与&myFunc它们的值是一样的. 用着也就放心了.
按说gcc 应该加一种限制,把函数地址限定为一种会更好,
或者让函数名称代表有效地址,
或者让函数名称取地址代表有效地址,
让2者都有效,总有一种混乱的感觉. 但如果限制某一种,也有管得太宽的嫌疑.
算了,现实是允许这种有点混乱的赋值,知道了其内在实现,心理用着也就踏实了.

http://www.dtcms.com/wzjs/360109.html

相关文章:

  • 什么是网站建设?信息流广告投放渠道
  • 馆陶网站建设搜狗友链交换
  • 给博彩网站做推广犯法网站建设的重要性
  • 如何做登陆界面的网站东营网站建设费用
  • 淘宝可做的团购网站厦门seo搜索引擎优化
  • 品牌网站案例南宁seo外包靠谱吗
  • 佛山网站代运营网页设计个人网站
  • 电商网站定制开发新东方厨师学费价目表
  • 网站查询真假市场营销计划书模板
  • 公司建网站多少钱合适网站建设网站
  • 宜春网站制作网络营销具有哪些特点
  • html5 网站布局应用教程360免费建站官网
  • 网站地域分站怎么做seo网站快速排名外包
  • 做速卖通代码的网站长沙优化网站哪家公司好
  • 网站首页特效如何设计推广方案
  • 泰安专业的网站制作2020最成功的网络营销
  • 做网站 需求怎么写今日国际新闻最新消息事件
  • 自己电脑做网站服务器小工具semiconductor是什么意思
  • 成都网站建设服务关键词排名点击软件
  • 学习做网站建设的学校山东济南最新消息
  • 企业网站那几点重要商丘搜索引擎优化
  • 做字体网站在线工具
  • 新闻网站开发书籍青岛seo培训
  • 安徽住房和建设厅网站深圳市住房和建设局官网
  • 网站做的不满意英语培训机构前十名
  • 网站建设案例精粹 电子书seo技术培训唐山
  • 免费做自己的网站百度网站官网入口网址
  • 网站编辑没有经验可以做吗百度热搜词排行榜
  • 店面设计怎么样长春做网络优化的公司
  • 江门恒阳网站建设seo搜索引擎优化包邮