你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

用C++做些编程比较重要的概念总结

[复制链接]
gaosmile 发布时间:2021-1-26 09:37

[导读] 最近使用C++做些编程,把日常遇到的些比较重要的概念总结分享一下。本文来分享一下模板类的原理,以及为什么需要模板类,使用时的基本要点。  x, f7 x( g& J& @. R, l: S

为什么需要模板

比如需要设计一个描述点的类,大致很快可以写成这样:

class Point_F
5 U# s# G8 ]6 f+ u  J1 i: ^: g{
! [- E) |# B' ~; I) M6 ipublic:
, K2 Z4 s2 L7 Y- u7 @3 _+ S5 ]  /*默认传入参数为0,0*/
5 {! ~, H- S( }* R8 k0 e Point_F(float x0 = 0, float y0 = 0)
) c$ Q% w) T1 L  :x(x0), y(y0) /*初始化列表*/8 X; f3 K) |7 n7 n  a( z2 Z( w& |; x5 l
{ }( w* N$ K1 D, o
  
/ m: l& H! X7 t3 G* N: F& W4 o  /*用const修饰函数,表示函数不会修改成员数据*/, X& ^6 x; U$ z1 _9 V* H4 a8 `
float get_x() const { return x; }
& `$ g. n& o& e8 x- g float get_y() const { return y; }
5 _& u( v8 R7 ]3 m9 [  F4 G& b: M+ |9 g0 B
private:7 V1 r; g3 Q# K  M& _
  /*一般会将数据放在私有区,以对外隐藏*/, P: ?& ~5 g5 c! W
float x;0 z8 C2 n, Q9 c' ^
float y;
' s# {% g6 z# f: `% S5 u+ E' X};* S4 c' a( ~& l0 _

可问题是,在有的场合这点的坐标系有可能不需要浮点,比如界面设计中点往往是整型表示即可,那此时就需要再设计一个整型成员类:

class Point_I. f& F3 a4 g6 r( W8 c# u# _
{
; k+ q. W7 Y3 M& Apublic:" d- B& H; U2 q/ d& |8 C! E' `
  /*默认传入参数为0,0*/
6 `0 o9 ]; a9 {6 k0 \( } Point_f(int x0 = 0, int y0 = 0)/ G7 ~4 @8 e; S6 l
  :x(x0), y(y0) /*初始化列表*/
7 F5 {& Y& F3 z& O { }
( R( o' Q" n8 ], k0 p6 i' ]) l  
1 R; m) \" X6 A: |; T  /*用const修饰函数,表示函数不会修改成员数据*/
% [2 K; O/ J4 x  Y! z2 x9 I( [) G: R int get_x() const { return x; }
  M- _' y$ l  t$ i8 A% n3 p6 R int get_y() const { return y; }% }/ u- h' f0 L" \& K1 s3 g
- D2 o& c6 [/ V, Q0 ?
private:& B! z6 u2 l# @) R- i. t' B; S
  /*一般会将数据放在私有区,以对外隐藏*/
% d$ {# j5 W' n+ n6 Y int x;- I- f6 h3 ~/ I8 n8 i& ^7 {
int y;
' t8 a0 {* C" e; q};: H- S/ r7 `% i

可是在应用代码中,往往发现对于不同数据成员的应用操作确实基本类似,而且应用代码往往这两种(甚至更多成员数据类型)都可能会同时用到,仅仅因为数据类型就需要笨笨的将原代码在改写一下,在现代高级语言中,这显然就比较机械了。

微信图片_20210126093526.png

C++中有没有可能将不同成员数据类型但是其顶层逻辑相同的对象,设计为一个类呢?就比如:

微信图片_20210126093532.png

C++模板编程正是为了解决这样的需求而设计的机制。该机制允许函数或类使用泛型类型(generic type)进行操作。从而,函数或类就可以处理许多不同的数据类型,而无需为每种数据类型重写相应的类或者函数。

怎么实现的呢?

这里又可以大致分这样三种情况:

  • 函数模板(Function templates)
  • 类模板(Class templates)
  • **成员模板(Member templates) **" a" N/ t; a9 ]& x3 t; W
函数模板

函数模板其基本语法范式为:

template <class identifier> function_declaration;1 k. w5 w" s0 @- d8 i# Y
template <typename identifier> function_declaration;$ j- b6 A) H' q+ C8 p4 U, f4 _, n: o" y
  • template 为模板关键字
  • <typename identifier> 、<class identifier> 定义函数参数泛型类型或函数体类变量泛型类型
    9 D8 r5 f8 t9 I2 `; `7 v- N3 O

比如:

#include <iostream>  T" p4 e1 k. n. q6 M4 i1 G6 |
using namespace std;
% L& B" s- R7 n8 i% L; l% G0 ?$ i0 l# S4 ~& \/ |3 j5 o% |
template <typename T>
$ h& o$ d5 \  @+ V! C* R) o+ UT max(T a, T b)
: Z3 ~, @4 `) K( I9 a{; T: C1 n' f0 c9 T" ?0 O6 b
return a > b ? a : b ;
" `# i% @1 w9 W. b}
/ f& H* {( H9 x' m; b  p- T" }

又或者写成如下形式:

#include <iostream># \) m" I* B; u$ N7 F- R
using namespace std;
/ |" }$ T) K- `4 S) O! U
$ v2 o' Y7 a6 M/ p5 z% h/ l+ Ptemplate <class T>
0 ^. g$ y; r8 u+ v( nT max(T a, T b)
1 c% X0 v- g0 x/ ]{
+ x9 V! V% p. F# h# d2 N7 V, V return a > b ? a : b ;* W: T+ ~$ a4 K
}' f- j, \* e) c

那么或许有的朋友会任务关键字class就意味着自定义类,而typename则是基本数据类型,比如int,float等,这样理解其实是不对的,从C++编译器的角度template <typename T>与template <class T>其语义是一样的,都是泛型,用户在使用这个模板函数的时候,所传入的参数都既可以是基本数据类型,也可以是类名。

对于上面的代码,或许初使用的朋友还会问,是不是可以随便传入类,这有可能编译不过。为什么呢?你传入的类需要支持>操作符,如果对于某个类你想使用该函数,而本身不支持>操作符,则需要实现>操作符。

类模板

与函数模板类似,类内部成员数据或者函数的参数或变量会使用,模板关键字定义的泛型名。比如:

template <typename T>; q, X9 n! p2 J( j' x
class Point_T/ ^6 Z9 L! d2 v0 O* g8 d) U8 e
{. b1 l" c1 N  I8 u, o
public:
' _% I  I5 }, |$ ^" l Point_T(T x0 = 0, T y0 = 0)8 G8 p8 U) u. q( E& `& D
  :x(x0), y(y0)# D& x+ F5 P! P  t! j3 C7 B$ d
{ }/ D2 T+ t* Z2 e4 \1 b8 W3 ~, K! ]
T get_x() const { return x; }# H& j. ?% w' V  ^. _; J, U
T get_y() const { return y; }0 \3 s: z1 |- d# f
6 ^% U% x5 e0 J
private:
: b# j2 v: q" N* Y1 ^) d1 m T x;3 V7 t5 d3 p7 ~7 X' x
T y;5 ~3 n3 ?4 `3 d
};- V1 C  C( Q& }; X& c

这小段代码就回答了之前提出的问题,可以支持不同数据类型的点。

int main()
7 R- t9 T+ E+ Q- l& |6 m: Z{7 _4 p7 c: @9 W. g, e  k  \3 H
Point_T<int>   p1(1, 2);) J) i! B; G: i( Y# S
Point_T<float> p2(1.1f, 2.2f);( @) S+ W- ]. R2 F. l& \% }( P; ^
cout << p1.get_x() << endl << p1.get_y() << endl;0 o! a) h( A& [0 h* k) ~. O, W7 M
cout << p2.get_x() << endl << p2.get_y() << endl;
4 a7 g, ]' U+ I! k, H5 P}* j) _+ k# e/ p* P  x/ v- M

以上述简单例子看,分别构造了整型点p1,以及浮点型点p2,那么究竟怎么做到的呢?为了理解得更清楚,这里将其关键汇编代码段拷贝下来简要看看:

        Point_T<int>   p1(1, 2);6 G" e' P9 P8 ?# H0 T
000C1D6C  push        2  
. J, J( F! \  w# e. x' l0 \000C1D6E  push        1  6 Y3 R5 Q8 |. |( X" ~7 r. F
000C1D70  lea         ecx,[p1]  + U4 x; ^- b! C8 a& \
000C1D73  call        Point_T<int>:oint_T<int> (0C11D1h)  
  m& p: _6 m. C1 N) t: o; ^+ R        Point_T<float> p2(1.1f, 2.2f);9 \; b. h3 U7 x3 t5 N
000C1D78  push        ecx  3 ]: w3 {5 S2 L1 \* ?
000C1D79  movss       xmm0,dword ptr [__real@400ccccd (0C7B34h)]  
3 ]/ e7 A2 T  v- S* d6 ]0 B/ C3 o( P000C1D81  movss       dword ptr [esp],xmm0  
0 ^9 x* ]9 Y7 m, m  D6 P" f000C1D86  push        ecx  ( P1 M1 i; ~# R
000C1D87  movss       xmm0,dword ptr [__real@3f8ccccd (0C7B30h)]  
" c! j7 E4 i4 F4 I# D8 h9 u( @000C1D8F  movss       dword ptr [esp],xmm0  - j$ K' ^  O: y( M4 `5 j+ s' @
000C1D94  lea         ecx,[p2]  
; J/ A* F4 i1 o! D4 e) E3 H000C1D97  call        Point_T<float>:oint_T<float> (0C1064h)
# ]5 f6 F& r1 V" F" U

可见编译器对不同类型参数实际上做了相应解析,相当于根据用户程序传入的参数编译出相应的多份代码。所以可以简单理解成编译器根据不同泛型实际参数类型生成了相应的处理代码。而前面所说的模板函数其原理也基本类似。

总结一下

通过些简单例子,梳理一下模板函数以及模板类的基本概念以及原理,理解了这两个概念,就比较容易理解成员模板。所谓泛型模板编程,其本质是编译器针对不同参数类型解析解析生成相应的处理代码。学会使用模板泛型编程你会发现你会少写很多代码,代码看起来会比较优雅,而其实操作起来也没有想象中那么难。


5 k! q3 T) S+ |
赞 收藏 评论0 发布时间:2021-1-26 09:37

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版