深化了解泛型编程的微妙-模板基础及代码实战-C (实现泛化)
一、C++模板概览
1.泛型编程的支持
C++不只为面向对象编程提供了言语支持,还支持泛型编程。正如第6章《设计可重用性》中讨论的,泛型编程的指标是编写可重用的代码。C++中支持泛型编程的基本工具是模板。虽然模板不严厉是面向对象的个性,但它们可以与面向对象编程结合发生弱小的成果。
2.模板的外围
在环节化编程范式中,关键编程单元是环节或函数。函数之所以有用,关键是由于它们准许你编写与特定值有关的算法,因此可以用于许多不同的值。例如,C++中的sqrt()函数计算调用者提供的值的平方根。只计算一个数字(如四)的平方根的函数不会特意有用!sqrt()函数是针对一个参数编写的,该参数是调用者传递的任何值的代表。
对象导向编程范式参与了对象的概念,对象将关系数据和行为组合在一同,但它并没有扭转函数和方法参数化值的形式。
3.模板的进阶参数化
模板将参数化概念进一步裁减,准许你对类型以及值启动参数化。C++中的类型包括int、double等基本类型,以及SpreadsheetCell、CherryTree等用户定义的类。有了模板,你可以编写不只与它将要给定的值有关,而且与这些值的类型有关的代码。例如,你可以编写一个堆栈类定义,而不是编写用于存储int、Cars和SpreadsheetCells的独自堆栈类,这个堆栈类定义可以用于任何这些类型。
4.模板的经常使用和关键性
虽然模板是一项惊人的言语个性,但C++中的模板在语法上或许令人困惑,许多程序员防止自己编写模板。但是,每个程序员至少须要知道如何经常使用模板,由于它们被宽泛用于库,例如C++规范库。本章教你如何在C++中支持模板,重点是在规范库中发生的方面。在此环节中,你将了解一些除了经常使用规范库之外,你可以在程序中运用的奇妙个性。
二、类模板
1.类模板的定义和运行
类模板定义了一个类,其中一些变量的类型、方法的前往类型和/或方法的参数被指定为模板参数。类模板关键用于容器,即存储对象的数据结构。这一节经过运转示例Grid容器来说明。为了坚持示例的正当长度并足够方便以说明特定要点,本章的不同局部将为Grid容器参与不在后续局部经常使用的配置。
2.编写类模板
假定你想要一个通用的游戏棋盘类,可以用作国内象棋棋盘、跳棋棋盘、井字棋棋盘或任何其余二维游戏棋盘。为了使其具备通用性,你应该能够存储国内象棋棋子、跳棋棋子、井字棋棋子或任何类型的游戏棋子。
三、不经常使用模板编写代码
1.经过多态性建设通用游戏棋盘
在没有模板的状况下,构建通用游戏棋盘的最佳方法是经常使用多态性来存储通用的GamePiece对象。而后,你可以让每个游戏的棋子从GamePiece类承袭。例如,在国内象棋游戏中,ChessPiece将是GamePiece的派生类。经过多态性,编写为存储GamePiece的GameBoard也可以存储ChessPiece。由于或许须要复制GameBoard,所以GameBoard须要能够复制GamePiece。这种成功经常使用多态性,所以一种处置打算是在GamePiece基类中参与一个纯虚构的clone()方法,派生类必定成功它以前往详细GamePiece的正本。
这是基本的GamePiece接口:
exportclassGamePiece{public:virtual~GamePiece()=default;virtualstd::unique_ptr<GamePiece>clone()const=0;};
GamePiece是一个形象基类。详细类,如ChessPiece,从它派生并成功clone()方法:
classChessPiece:publicGamePiece{public:std::unique_ptr<GamePiece>clone()constoverride{//调用复制结构函数来复制这个实例returnstd::make_unique<ChessPiece>(*this);}};
2.GameBoard的成功
GameBoard的成功经常使用向量的向量和unique_ptr来存储GamePieces:
GameBoard::GameBoard(size_twidth,size_theight):m_width{width},m_height{height}{m_cells.resize(m_width);for(auto&column:m_cells){column.resize(m_height);}}GameBoard::GameBoard(constGameBoard&src):GameBoard{src.m_width,src.m_height}{//Thector-initializerofthisconstructordelegatesfirsttothe//non-copyconstructortoallocatetheproperamountofmemory.//Thenextstepistocopythe>GameBoardchessBoard{8,8};autopawn{std::make_unique<ChessPiece>()};chessBoard.at(0,0)=std::move(pawn);chessBoard.at(0,1)=std::make_unique<ChessPiece>();chessBoard.at(0,1)=nullptr;
这个GameBoard类运转得相当好,它可以用于国内象棋棋盘的创立和棋子的搁置。
四、类模板成功的Grid类
1.GameBoard的局限性
在上一节中的GameBoard类虽然适用,但有其局限性。首先,你无法经常使用GameBoard来按值存储元素;它总是存储指针。更重大的疑问与类型安保有关。GameBoard中的每个单元格都存储一个unique_ptr<GamePiece>。即使你存储的是ChessPieces,当你经常使用at()恳求特定单元格时,你将获取一个unique_ptr<GamePiece>。这象征着你必定将检索到的GamePiece向下转型为ChessPiece能力经常使用ChessPiece的特定配置。GameBoard的另一个缺陷是它不能用于存储原始类型,如int或double,由于单元格中存储的类型必定派生自GamePiece。
2.成功通用Grid类
因此,假设你能编写一个通用的Grid类来存储ChessPieces、SpreadsheetCells、ints、doubles等就很好了。在C++中,你可以经过编写类模板来成功这一点,这准许你编写一个不指定一个或多个类型的类。而后客户端经过指定他们想要经常使用的类型来实例化模板。这就是所谓的泛型编程。
3.泛型编程的长处
泛型编程的最大长处是类型安保。类及其方法中经常使用的类型是详细类型,而不是像上一节中多态处置打算那样的形象基类类型。例如,假定不只要ChessPiece,还有TicTacToePiece:
classTicTacToePiece:publicGamePiece{public:std::unique_ptr<GamePiece>clone()constoverride{//调用复制结构函数来复制这个实例returnstd::make_unique<TicTacToePiece>(*this);}};
经常使用上一节中的多态处置打算,没有什么能阻止你在同一个棋盘上存储井字棋棋子和国内象棋棋子:
GameBoardchessBoard{8,8};chessBoard.at(0,0)=std::make_unique<ChessPiece>();chessBoard.at(0,1)=std::make_unique<TicTacToePiece>();
这样做的一个大疑问是,你须要记住一个单元格存储了什么,以便在调用at()时口头正确的向下转型。
五、Grid类模板的定义
1.类模板的语法
为了了解类模板,审核其语法十分有协助。以下示例展现了如何将GameBoard类修正为模板化的Grid类。代码前面会详细解释语法。请留意,类名已从GameBoard改为Grid。
2.经常使用值语义成功Grid类
与GameBoard成功中经常使用的多态指针语义相比,我选用经常使用值语义而不是多态来成功这个处置打算。m_cells容器存储实践对象,而不是指针。与指针语义相比,经常使用值语义的一个缺陷是不能有真正的空单元格;也就是说,单元格必定一直蕴含某个值。经常使用指针语义时,空单元格存储nullptr。std::optional在这里提供了协助。它准许你在依然有示意空单元格的方法的同时经常使用值语义。
template<typenameT>classGrid{public:explicitGrid(size_twidth=DefaultWidth,size_theight=DefaultHeight);virtual~Grid()=default;//Explicitlydefaultacopyconstructorandassignmentoperator.Grid(constGrid&src)=default;Grid&operator=(constGrid&rhs)=default;//Explicitlydefaultamoveconstructorandassignmentoperator.Grid(Grid&&src)=default;Grid&operator=(Grid&&rhs)=default;std::optional<T>&at(size_tx,size_ty);conststd::optional<T>&at(size_tx,size_ty)const;size_tgetHeight()const{returnm_height;}size_tgetWidth()const{returnm_width;}staticconstsize_tDefaultWidth{10};staticconstsize_tDefaultHeight{10};private:voidverifyCoordinate(size_tx,size_ty)const;std::vector<std::vector<std::optional<T>>>m_cells;size_tm_width{0},m_height{0};};
3.类模板的详细解读
exporttemplate<typenameT>:这一行示意接上去的类定义是一个对于类型T的模板,并且它正在从模块中导出。template和typename是C++中的关键字。如前所述,模板在类型上参数化,就像函数在值上参数化一样。
经常使用模板类型参数名(如T)来示意调用者将作为模板类型参数传递的类型。T的称号没有不凡含意——你可以经常使用任何你想要的称号。
4.对于模板类型参数的留意事项
出于历史要素,你可以经常使用关键字class而不是typename来指定模板类型参数。因此,许多书籍和现有程序经常使用相似template<classT>的语法。但是,在这种状况下经常使用class这个词是令人困惑的,由于它暗示类型必定是一个类,这实践上并不正确。类型可以是类、结构体、联结、言语的原始类型,如int或double等。
六、Grid类模板与GameBoard类的对比
1.数据成员的变动
在之前的GameBoard类中,m_cells数据成员是指针的向量的向量,这须要不凡的复制代码,因此须要拷贝结构函数和拷贝赋值操作符。在Grid类中,m_cells是可选值的向量的向量,所以编译器生成的拷贝结构函数和赋值操作符是可以的。
2.显式自动结构函数和操作符
一旦你有了用户申明的析构函数,就不介绍编译器隐式生成拷贝结构函数或拷贝赋值操作符,因此Grid类模板显式地将它们自动化。它还显式自动化了移动结构函数和移动赋值操作符。以下是显式自动的拷贝赋值操作符:
Grid&operator=(constGrid&rhs)=default;
可以看到,rhs参数的类型不再是constGameBoard&,而是constGrid&。在类定义内,编译器会在须要时将Grid解释为Grid<T>,但假设你情愿,也可以显式地经常使用Grid<T>:
Grid<T>&operator=(constGrid<T>&rhs)=default;
但是,在类定义外,你必定经常使用Grid<T>。当你编写类模板时,你过去以为的类名(Grid)实践上是模板名。当你想议论实践的Grid类或类型时,你必定经常使用模板ID,即Grid<T>,这些是针对特定类型(如int、SpreadsheetCell或ChessPiece)的Grid类模板的实例化。
3.at()方法的降级
由于m_cells不再存储指针,而是存储可选值,at()方法如今前往std::optional<T>而不是unique_ptrs,即可以有类型T的值,也可以为空的optionals:
std::optional<T>&at(size_tx,size_ty);conststd::optional<T>&at(size_tx,size_ty)const;
七、Grid类模板的方法定义
1.模板方法定义格局
每个Grid模板的方法定义都必定以template<typenameT>说明符扫尾。结构函数如下所示:
template<typenameT>Grid<T>::Grid(size_twidth,size_theight):m_width{width},m_height{height}{m_cells.resize(m_width);for(auto&column:m_cells){column.resize(m_height);}}
留意:类模板的方法定义须要对经常使用该类模板的任何客户端代码可见。这对方法定义的位置施加了一些限度。通常,它们间接放在类模板定义自身的同一文件中。本章前面讨论了绕过这一限度的一些方法。
2.类名和方法定义
请留意,::前的类名是Grid<T>,而不是Grid。在一切方法和静态数据成员定义中,你必定指定Grid<T>作为类名。结构函数的主体与GameBoard结构函数相反。其余方法定义也相似于GameBoard类中的对应方法,但有适当的模板和Grid<T>语法变动:
template<typenameT>voidGrid<T>::verifyCoordinate(size_tx,size_ty)const{if(x>=m_width){throwstd::out_of_range{std::format("{}mustbelessthan{}.",x,m_width)};}if(y>=m_height){throwstd::out_of_range{std::format("{}mustbelessthan{}.",y,m_height)};}}template<typenameT>conststd::optional<T>&Grid<T>::at(size_tx,size_ty)const{verifyCoordinate(x,y);returnm_cells[x][y];}template<typenameT>std::optional<T>&Grid<T>::at(size_tx,size_ty){returnconst_cast<std::optional<T>&>(std::as_const(*this).at(x,y));}
3.类模板方法的自动值
留意:假设类模板方法的成功须要某个模板类型参数的自动值(例如T),则可以经常使用T{}语法。T{}调用对象的自动结构函数(假设T是类类型),或生成零(假设T是基本类型)。这种语法称为零初始化语法。它是为尚不知道类型的变量提供正当自动值的好方法。
八、经常使用Grid类模板
1.模板实例化
当你想要创立Grid对象时,不能独自经常使用Grid作为类型;你必定指定将存储在该Grid中的类型。为特定类型创立类模板对象称为实例化模板。以下是一个示例:
Grid<int>myIntGrid;//申明一个存储int的网格,经常使用结构函数的自动参数。Grid<double>myDoubleGrid{11,11};//申明一个11x11的double类型网格。myIntGrid.at(0,0)=10;intx{myIntGrid.at(0,0).value_or(0)};Grid<int>grid2{myIntGrid};//拷贝结构函数Grid<int>anotherIntGrid;anotherIntGrid=grid2;//赋值操作符
请留意myIntGrid、grid2和anotherIntGrid的类型是Grid<int>。你不能在这些网格中存储SpreadsheetCells或ChessPieces;假设尝试这样做,编译器将生成失误。
2.经常使用value_or()
还要留意value_or()的经常使用。at()方法前往一个可选援用,或许蕴含值也或许不蕴含。value_or()方法在可选项中有值时前往该值;否则,它前往给value_or()的参数。
3.模板类型的关键性
模板类型的指定十分关键;以下两行都无法编译:
Gridtest;//无法编译Grid<>test;//无法编译
假设你想申明一个接受Grid对象的函数或方法,你必定在Grid类型中指定存储在网格中的类型:
voidprocessIntGrid(Grid<int>&grid){/*省略注释以繁复*/}
或许,你可以经常使用本章前面讨论的函数模板,编写一个模板化的函数,该函数依据网格中元素的类型启动模板化。
留意:你可以经常使用类型别名来简化完整的Grid类型的重复书写,例如Grid<int>:
usingIntGrid=Grid<int>;voidprocessIntGrid(IntGrid&grid){/*注释*/}
4.Grid类模板的多样性
Grid类模板可以存储的不只仅是int。例如,你可以实例化一个存储SpreadsheetCells的Grid:
Grid<SpreadsheetCell>mySpreadsheet;SpreadsheetCellmyCell{1.234};mySpreadsheet.at(3,4)=myCell;
你也可以存储指针类型:
Grid<constchar*>myStringGrid;myStringGrid.at(2,2)="hello";
指定的类型甚至可以是另一个模板类型:
Grid<vector<int>>gridOfVectors;vector<int>myVector{1,2,3,4};gridOfVectors.at(5,6)=myVector;
你还可以在自在存储区灵活调配Grid模板实例:
automyGridOnFreeStore{make_unique<Grid<int>>(2,2)};//自在存储区上的2x2网格。myGridOnFreeStore->at(0,0)=10;intx{myGridOnFreeStore->at(0,0).value_or(0)};
什么是C++
C++这个词在中国大陆的程序员圈子中通常被读做“C加加”,而西方的程序员通常读做“C plus plus”,“CPP”。 它是一种使用非常广泛的计算机编程语言。 C++是一种静态数据类型检查的、支持多重编程范式的通用程序设计语言。 它支持过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计等多种程序设计风格。 在C基础上,一九八三年又由贝尔实验室的Bjarne Strou-strup推出了C++。 C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。 C++目前流行的编译器最新版本是Borland C++4.5,Symantec C++6.1,和Microsoft VisualC++ 2012。 C++提出了一些更为深入的概念,它所支持的这些面向对象的概念容易将问题空间直接地映射到程序空间,为程序员提供了一种与传统结构程序设计不同的思维方式和编程方法。 因而也增加了整个语言的复杂性,掌握起来有一定难度。 C++由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。 开始,C++是作为C语言的增强版出现的,从给C语言增加类开始,不断的增加新特性。 虚函数(virtual function)、运算符重载(operator overloading)、多重继承(multiple inheritance)、模板(template)、异常(exception)、RTTI、命名空间[1](name space)逐渐被加入标准。 [2]1998年国际标准组织(international standard organization, ISO)颁布了C++程序设计语言的国[3]际标准ISO/IEC 1988-1998。 C++是具有国际标准的编程语言,通常称作ANSI/ISOC++。 [4]1998年是C++标准委员会成立的第一年,以后每5年视实际需要更新一次标准。 C++0x最终国际投票已于2011年8月10日结束,并且所有国家都投出了赞成票,C++0x已经毫无疑义地成为正式国际标准。 先前被临时命名为C++0x的新标准将被称为C++ 2011。 C++ 2011将取代现行的C++标准ISO/IEC ,它公开于1998年并于2003年更新,通称C++98以及C++03。 国际标准化组织于2011年9月1日出版发布ISO/IEC :2011,名称是:Information technology -- Programming languages -- C++ Edition: 3。 计算机诞生初期, 人们要使用计算机必须用机器语言或汇编语言编写程序。世界上第一种计算机高级语言诞生于1954年, 它是FORTRAN语言。 先后出现了多种计算机高级语言。 其中使用最广泛、影响最大的当推BASIC语言和C语言。BASIC语言是1964年在FORTRAN语言的基础上简化而成的, 它是为初学者设计的小型高级语言。C语言是1972年由美国贝尔实验室的研制成功的。 它不是为初学者设计的,而是为计算机专业人员设计的。 大多数系统软件和许多应用软件都是用C语言编写的。但是随着软件规模的增大, 用C语言编写程序渐渐显得有些吃力了。C++是由AT&T Bell(贝尔)实验室的Bjarne Stroustrup博士及其同事于20世纪80年代初在C语言的基础上开发成功的。 C++保留了C语言原有的所有优点, 增加了面向对象的机制。C++是由C发展而来的, 与C兼容。 用C语言写的程序基本上可以不加修改地用于C++。 从C++的名字可以看出它是C的超越和集中。 C++既可用于面向过程的结构化程序设计, 又可用于面向对象的程序设计, 是一种功能强大的混合型的程序设计语言。C++对C的“增强”,表现在六个方面:(1) 类型检查更为严格。 (2) 增加了面向对象的机制。 (3)增加了泛型编程的机制(template)(4)增加了异常处理(5)增加了运算符重载(6)增加了标准模板库(STL)面向对象程序设计,是针对开发较大规模的程序而提出来的,目的是提高软件开发的效率。不要把面向对象和面向过程对立起来, 面向对象和面向过程不是矛盾的,而是各有用途、互为补充的。学习C++, 既要会利用C++进行面向过程的结构化程序设计, 也要会利用C++进行面向对象的程序设计,更要会利用模板进行泛型编程。C和C++关系但是,C是C++的基础,C++语言和C语言在很多方面是兼容的。 因此,掌握了C语言,再进一步学习C++就能以一种熟悉的语法来学习面向对象的语言,从而达到事半功倍的目的。 C timeline1978 k&R C---->1988 ANSI C-->1995 ISO C学习C语言最经典的还是The C Programming Language发展历史C++语言发展大概可以分为三个阶段:第一阶段从80年代到1995年。 这一阶段C++语言基本上是传统类型上的面向对象语言,并且凭借着接近C语言的效率,在工业界使用的开发语言中占据了相当大份额;第二阶段从1995年到2000年,这一阶段由于标准模板库(STL)和后来的Boost等程序库的出现,泛型程序设计在C++中占据了越来越多的比重性。 当然,同时由于Java、C#等语言的出现和硬件价格的大规模下降,C++受到了一定的冲击;第三阶段从2000年至今,由于以Loki、MPL等程序库为代表的产生式编程和模板元编程的出现,C++出现了发展历史上又一个新的高峰,这些新技术的出现以及和原有技术的融合,使C++已经成为当今主流程序设计语言中最复杂的一员。 [7]优点C++代码· C++设计成静态类型、和C同样高效且可移植的多用途程序设计语言。 · C++设计成直接的和广泛的支持多种程序设计风格(程序化程序设计、资料抽象化、面向对象程序设计、泛型程序设计)。 · C++设计成给程序设计者更多的选择,即使可能导致程序设计者选择错误。 · C++设计成尽可能与C兼容,借此提供一个从C到C++的平滑过渡。 · C++避免平台限定或没有普遍用途的特性。 · C++不使用会带来额外开销的特性。 · C++设计成无需复杂的程序设计环境。 [8]出于保证语言的简洁和运行高效等方面的考虑,C++的很多特性都是以库(如STL)或其他的形式提供的,而没有直接添加到语言本身里。 关于此类话题,Bjarne Stroustrup的《C++语言的设计和演化》(1994)里做了详尽的陈述。 C++在一定程度上可以和C语言很好的结合,甚至目前大多数C语言程序是在C++的集成开发环境中完成的。 C++相对众多的面向对象的语言,具有相当高的性能。 C++引入了面向对象的概念,使得开发人机交互类型的应用程序更为简单、快捷。 很多优秀的程序框架包括MFC、QT、wxWidgets就是使用的C++。 [9]代码性能人们一般认为,使用Java或C#的开发成本比C++低。 但是,如果充分分析C++和这些语言的差别,会发现这句话的成立是有条件的。 这个条件就是:软件规模和复杂度都比较小。 如果不超过3万行有效代码(不包括生成器产生的代码),这句话基本上还能成立。 否则,随着代码量和复杂度的增加,C++的优势将会越来越明显。 造成这种差别的就是C++的软件工程性。 [9]缺点C++由于语言本身过度复杂,这甚至使人类难于理解其语义。 更为糟糕的是C++的编译系统受到C++的复杂性的影响,非常难于编写,即使能够使用的编译器也存在了大量的问题,这些问题大多难于被发现。 [9]由于本身的复杂性,复杂的C++程序的正确性相当难于保证。 也有人提出不支持多线程的原语等缺陷。 不过有如此多的知名人士提出了如此多的缺陷,正说明C++被广泛使用和成功。 c++语言由于过度的复杂性,以及与unix的文化相抵触,在unix/linux领域受到很多著名人士(比如Linux之父linus torvalds与著名黑客Eric S. Raymond)的强烈批评与抵制。 C++基本数据类型和表达式数据是程序处理的对象,数据可以依其本身的特点进行分类。 我们知道在数学中有整数、实数概念,在日常生活中需要用字符串来表示人的姓名和地址,有些问题的回答只能是“是”或“否”(即逻辑“真”或“假”)。 不同类型的数据有不同的处理方法,例如:整数和实数可以参加算术运算,但实数的表示又不同于整数,要保留一定的小数位;字符串可以拼接;逻辑数据可以参加“与”、“或”、“非”等逻辑运算。 我们编写计算机程序,目的就是为了解决客观世界中的现实问题。 所以,高级语言中也为我们提供了丰富的数据类型和运算。 C++中的数据类型分为基本类型和自定义类型。 基本类型是C++编译系统内置的。 基本数据类型C++的基本数据类型如下表所示(表中各类型的长度和取值范围,以面向IA-32处理器的VC++ 2008和gcc 4.2为标准)。 类型名长度(字节)取值范围bool1false,truechar 1-128~127signed char1-128~127unsigned char10~255short(signed short)2-~unsigned short20~int(signed int)4-~unsigned int40~long(signed long)4-~unsigned long40~float43.4X10^(-38)~3.4X10^(38)double81.7X10^(-308)~1.7X10^(308)long double81.7X10^(-308)~1.7X10^(308)编辑本段编程技巧new和delete运算符new和delete提供了存储的动态内存分配和释放功能,它的作用相当于C语言的函数malloc()和free(),但是性能更为优越。 使用new较之使用malloc()有以下的几个优点:(1)new自动计算要分配类型的大小,不使用sizeof运算符,比较省事,可以避免错误。 (2)自动地返回正确的指针类型,不用进行强制指针类型转换。 (3)可以用new对分配的对象进行初始化。 [11]inline对于频繁使用的函数,C语言建议使用宏调用代替函数调用以加快代码执行,减少调用开销。 但是宏调用有许多的弊端,可能引起不期望的副作用。 例如宏:#define abs(a)(a)<0?(-a):(a)),当使用abs(i++)时,这个宏就会出错。 所以在C++中应该使用inline内联函数替代宏调用,这样既可达到宏调用的目的,又避免了宏调用的弊端。 使用内联函数只须把inline关键字放在函数返回类型的前面。 [11]函数重载在C语言中,两个函数的名称不能相同,否则会导致编译错误。 而在C++中,函数名相同而参数数据类型不同或参数个数不同或二者皆不同的两个函数被解释为重载。 使用函数重载可以帮助程序员处理更多的复杂问题,避免了使用诸如intabs()、fabs()、dabs()等繁杂的函数名称;同时在大型程序中,使函数名易于管理和使用,而不必绞尽脑汁地去处理函数名。 同时必须注意,参数数据类型相同,但是函数返回类型不同的两个函数不能重载。 [11]参数传递在C语言中,如果一个函数需要修改用作参数的变量值的时候 ,参数应该声明为指针类型;当参数的大小超过一个机器字长时,通过传值方式来传递参数的效率较低,也需要用指针。 由于C语言的指针可以进行p++,--p,p+=1等算术运算,所以编译器无法在编译的时候确定指针引用的变量。 对于复杂的程序,使用指针容易出错,程序也难以读懂。 在C++中,对于上述情况 可以使用引用来代替指针,使程序更加清晰易懂。 引用就是对变量取的一个别名,对引用进行操作,这就相当于对原有变量进行操作。 [11]缺省参数在C++中函数可以使用缺省参数。 通常的情况下,一个函数应该具有尽可能大的灵活性。 使用缺省参数为程序员处理更大的复杂性和灵活性问题提供了有效的方法,所以在C++的代码中都大量地使用了缺省参数。 需要说明的是,所有的缺省参数必须出现在不缺省参数的右边。 亦即,一旦开始定义缺省参数,就不可再说明非缺省的参数。 否则当你省略其中一个参数的时候,编译器无法知道你是自定义了这个参数还是利用了缺省参数而定义了非缺省的参数。 [11]使用STLSTL(Standard Template Library,标准模板库), STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),并包括一些工具类如auto_ptr。 几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。 作用符被重载,使得我们可以像访问数组一样访问vector中的元素。 [11]使用模板.模板的概念。 模板是C++的一个特性,是函数和类可以作用于不同的类型上而不需要针对每一个具体类型重复相同的代码。 与模板相反,我们已经学过的重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。 正确的调用重载函数。 例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。 如果使用模板就可以只写一个通用的MAX模板,而不需要针对每个类型重复相同的逻辑。 指针与引用的区别指针与引用看上去完全不同(指针用操作符“*”和“->”,引用使用操作符“&”),但是它们似乎有相同的功能。 指针与引用都是让你间接引用其他对象。 你如何决定在什么时候使用指针,在什么时候使用引用呢?首先,要认识到在任何情况下都不能使用指向空值的引用。 一个引用必须总是指向某些对象。 因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。 相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。 [12]
c++的多泛型设计
根据Effective C++第三版第一条款的描述,C++由以下四个“子语言”组成:1、C子语言。 C++支持C语言的几乎全部功能,主要是c89的部分,在语法上与C语言仅有极微妙的差别(如括号表达式的左右值性,具体请参考C++标准文献)。 这部分功能对应于传统的面向过程的编程泛型,并提供了面向函数编程泛型的基础。 2、面向对象的C++语言。 C++语言原本不具备面向对象的设计功能,然而随着面向对象编程的概念的普及,C++语言也开发出了支持面向对象功能的版本。 这部分功能对应于面向对象的编程泛型。 3、泛型编程语言。 C++强大(但容易失控的)模板功能使它能在编译期完成许多工作,从而大大提高运行期效率,并且大大提高了C++的表达能力。 STL(C++标准模板库,Standard Template Library)是一个基于模板技术的库。 随着STL的不断发展,它已经逐渐成为C++程序设计中不可或缺的部分,其效率可能比一般的native代码低些,但是其安全性与规范性使它大受欢迎。 模板使C++能够支持泛型编程(generic programming)和生成式编程(generative programming)的泛型。 4、在C++11中引入的Lambda,使得程序员可以定义匿名函数,完善了C++对于面向函数的编程泛型的支持。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。