编程学习(五)----c++总体概览(二)


    这一篇文章来简单说说泛型设计、基于异常的设计、用其他名字来命名数组、标准数组(向量)。这些内容在后面都会慢慢提现出来,这里看懂也好,看不懂...嘿嘿,当然不能“看不懂也罢”了,看不懂就回头来重新看一遍,我写下来的都是自己看懂的,为的是巩固自己学习的东西,至于看不懂的部分我还会继续看书去解决之道看懂为止。不管怎样一定要坚持就好了。下面来看看泛型设计:

泛型设计:

概念:通过泛型思想进行的程序设计就是泛型设计。

    这个概念刚开始我也觉得很空泛很难理解,后来看了一些例子也就慢慢的明白了,下面就用容易理解的话来解释一下这个概念吧。比如下面两个语句:

void int_sort(int array[],int num)//num表示元素个数

void string_sort(string array[],int num)

若是采用相同的排序算法,则它们内部的流程处理机制是相同的。若是在编码的阶段只用一个类和函数就能描述它们的功能,通过使用不同的实际类型带入来处理不同的数据,那么将会很大程度上简化程序的设计和编码工作,如果只是针对上面两个语句的话,那么这种简化提现就不是那么明显,一旦数据量多了、大了的话,这个简化优势就很明显了。在这样的思想里面,很多基本算法都被抽象,独立于它们所操作的数据,用于以相同或者是相似的方式处理各种不同类型的数据,甚至是未知的数据类型。在程序设计中,一个程序实体的这种能对多种类型的数据进行操作或描述的特性就称之为泛型(或类属)。

在进行泛型程序的设计之前必须要了解c++的模板机制。可以不太标准的说法:模板机制就是泛型设计。什么是模板(template)呢?简单的说就是一个可以提供给程序设计使用的函数模型。

1、函数模板的语法:

template<classT1,classT2,......>

<返回值类型><函数名>(<参数列表>){

............

}

例:template<classT1>

    T1 whichMax(T1a,T1b){

         return(a>b?a:b);

    }

函数模板的调用语法;

函数名<<类型名1>,<类型名2>,......>(实参列表)

这里的<类型名1>,<类型名2>,......分别替换函数模板定义中的T1、T2...

例:int main(){

    int a=3,b=4;

    int m=whichMax<int>(3,4);//判断两个整型数据大小关系

    double d1=3.14,d2=4.96;

    double dm=whichMax<double>(d1,d2);//判断俩double数据大小关系

    }

当然也可以不显示提供T1、T2等类型,例如上例中省略掉<int>、<double>也是可以的,但是最好还是写上。

2、函数模板的实例化

要使用模板函数就首先要对函数模板实例化,所谓实例化就是指生成具体的函数。其实这一系列的实例化可以看成是函数的重载,因为函数名都相同。例子在这里就不举了,上面两个小例的结合就是函数模板的实例化了。

3、函数模板的非类型参数

在函数模板中,除了类型参数之外,还可以有非类型参数,什么是非类型参数呢?来看个例子:

template<class T1,int esize>

void g(T1 a){

t1 temp[esize];

......

}

这时要使用函数模板,就必须显示指定模板参数,其调用为:

f<int,10>();

4、函数模板的重载

有时候,为了弥补函数模板缺乏的灵活性,需要把函数模板与函数重载结合起来使用,可是在实际运用中,这样的重载会造成代码的膨胀与不可修改的warning.所以在这里就简单的了解一下就可以了,有兴趣的可以自己琢磨琢磨。

*********************************************************************************************************************************

**  在函数模板whichMax中,若调用为:                                                           

**  char*s1=”aa”;                                                                                                    

**  char*s2=”bb”;                                                                                                    

**  whichMax(s1,s2);                                                                                             

**  试图对字符串大小进行比较,但是调用的结果不对。因为编译器会生成以下实例:                                                                                                     

**  char*whichMax(char*a,char*b){return(a>b?a:b);}                        

**  直接对字符串的大小进行比较本来就是不对的,所以可以额外提供一个具体类型的whichMax函数:                                                                           

**  char*whichMax(char*a,char*b){return strcmp(a,b)>0 a:b;}     

**  此时,程序中有两个whichMax,一个是模板函数,一个是带两个char*参数的whichMax这两者之间就是重载关系 。                                    

********************************************************************************************************************************

既然函数有模板,那么类也有模板。如果要定义的多个类功能相同,成员也相同,只是成员函数类型不同的话,就可以采用类模板来统一描述这些类。模板类的定义与类的定义没有什么不同,格式都差不多:

template<class T1,class T2,......>

class<类名>{

    成员说明

};

既然类可以像函数那样写成模板的话,那么同样的它也可以进行实例化操作。编译器同样是根据给出的参数类型对类进行实例化操作,同时给出一个实例化后的对象。

基于异常的设计:

    所谓的异常就是程序在运行的时候出现的反常情况。比如说数组下标越界、打开文件失败等等。一个程序针对这些异常做出的处理就叫做异常处理机制。这个机制的构成如下:

1、程序中异常出现的点。这时就需要程序抛出异常(rise或throw)

            if(!infile){

               string errMsg(unable to open file:);

               errMsg+=fileName;

               throw errMsg;

              }

2、程序中异常被处理的点。在c++中,异常的处理由catch语句来执行。

              catch( string exceptionMsg ) {

                log_message( exceptionMsg );

                return false;

                 }

有种特殊的、能够处理全部异常的catch子句如下:

     catch...

     {

       //虽然它无法访问异常对象,但是可以处理所有异常

       //我们可以把它看作是一种捕捉所有异常的catch子句

      }

这里只是简单的介绍这么多,后面还会详细的讲到的。

用其他名字来命名数组:

我们可能会经常遇到这种情况:把我们写好的代码发给其它部门使用的时候导致出现错误。这是怎么回事呢?这是因为在全局可见的名字不是唯一的,使代码产生了互斥。当然,我们想到的方法就是加上一个限定的字符前缀保证唯一性。例如:class array{...};改写成class Cplusplus_primer_array{...};

虽然有可能这个名字是唯一的,但是并不一定。所以,我们可以使用一个可替代的、更短的或更一般的名字与一个现有的名字空间关联起来。例如:

       namespace LIB=IBM_canada_Laboratory;

       namespace DFA=Disney_Feature_Animation;

以上就是简单粗略的点到了命名空间机制,有兴趣的朋友可以查阅相关专业的资料来深入了解C++中命名空间机制是如何在程序中提现它的强大的。

标准数组——————向量

向量,用英文表示就是vector。它是一个类模板,也是一个数组类,是C++标准库的一部分。

例如:vector<int>ivec(10);定义了一个包含10个整型对象的向量

  vector<string>svec(10);定义了一个包含10个字符串对象向量

要定义一个向量我们必须包含相关的头文件

#include < vector >

下面都是vector 对象的合法定义

#include < vector >

// 创建vector 对象的各种方法

vector<int> veco; // 空的vector

const int size = 8;

const int value = 1024;

vector<int> vec1( size );// size vector每个元素都被初始化为0

vector<int> vec2(size,value);// size8vector每个元素都被动始化为1024

int ia[4] = { 0,1,1,2 };

vector<int> vec3( ia,ia+4 );// vtc3 size 4被初始化为ia 个值

vector<int> vec4( vec2 );// vec4 vec2 的拷贝

    既然定义了向量我们就需要遍历里边的元素与Array 类模板一样标准vector 类模板也支持使用下标操作符例如

#include <vector>

extern int getSize();

void mumble()

{

int size = getSize();

vector< int > vec( size );

for ( int ix = 0; ix < size; ++ix )

vec[ ix ] = ix;

// ...

}

    另外一种遍历方法是使用迭代器对(iterator pair )来标记向量的起始处和结束处。迭代器是一个支持指针类型抽象的类对象vector 类模板提供了一对操作begin()和end() 它们分别返回指向向量开始处和结束处后1 个的迭代器这一对迭代器合起来可以标记出待遍历元素的范围例如下面的代码是前面代码段的一个等价实现:

#include < vector >

extern int getSize();

void mumble()

{

int size = getSize();

vector< int > vec( size );

vector< int >::iterator iter = vec.begin();

for ( int ix = 0; iter != vec.end(); ++iter, ++ix )

*iter = ix;

// ...

}

iter 的定义

vector< int >::iterator iter = vec.begin();

将其初始值指向vec 的第一个元素iterator 是vector 类模板中用typedef 定义的类型

而这里的vector 类实例包含int 类型的元素下面的代码使迭代器指向vector 的下一个元

++iter

下面的代码解除迭代器的引用以便访问实际的元素

*iter

能够应用到向量上的操作惊人地多但是它们并不是作为vector 类模板的成员函数提供

的它们是以一个独立的泛型算法集的形式由标准库提供下面是一组可供使用的泛型算法的示例

搜索算法:

find()find_if()search()binary_search() count()和count_if()

分类排序与通用排序算法:

sort() partial_sort() merge()partition() rotate() reverse()和random_shuffle()

删除算法:unique()和remove()

算术算法accumulate()partial_sum()inner_product()和adjacent_difference()

生成和变异算法generate() fill() transformation()copy()和for_each()

关系算法equal() min()和max()

泛型算法接受一对迭代器它们标记了要遍历元素的范围例如ivec 是一个包含某种类型元素的某个长度的向量要用sort()对它的全部元素进行排序我们只需简单地这样写

sort( ivec.begin(), ivec.end() );

只想对ivec 向量的前面一半进行排序可以这样写

sort( ivec.begin(), ivec.begin()+ivec.size()/2 );

泛型算法还能接受指向内置数组的指针对例如已知数组

int ia[7] = { 10, 7, 9, 5, 3, 7, 1 };

我们可以如下对整个数组排序

sort( ia, ia+7 );

我们还可以只对前四个元素排序

sort( ia, ia+4 );

要使用这些算法我们必须包含与它们相关的头文件

#include <algorithm>

下面的代码显示了怎样把各种各样的泛型算法应用到vector 类对象上

#include <vector>

#include <algorithm>

#include <iostream>

int ia[ 10 ] = {

51, 23, 7, 88, 41, 98, 12, 103, 37, 6 };

int main()

{

vector< int > vec( ia, ia+10 );

// 排序数组

sort( vec.begin(), vec.end() );

// 获取值

int search_value;

cin >> search_value;

// 搜索元素

vector<int>::iterator found;

found = find( vec.begin(), vec.end(), search_value );

if ( found != vec.end() )

cout << "search_value found!\n";

else cout << "search_value not found!\n";

// 反转数组

reverse( vec.begin(), vec.end() );

// ...

}

标准库还提供了对map 关联数组的支持即数组元素可以被整数值之外的其他东西索引。

例如我们可以这样来支持一个电话目录这个电话目录是电话号码的数组但是它的元素可以由该号码所属人的姓名来索引

#include <map>

#include <string>

#include "TelephoneNumber.h"

map< string, telephoneNum > telephone_directory;

    终于写完了这部分的内容,后面关于vector这一部分其实我自己都看的有点儿模糊,所以还必须回过头来再认真看看才行。后面的内容就是针对这一篇与上一篇文章内容进行的更详细的内容介绍与延伸,逐步探讨C++中基本、但又非常先进的特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值