C++进阶基础

类和对象的基础

类的一般定义格式

class <类名>
{
public:
Private:
Protected:
};
#include <iostream>
using namespace std;

class Tpoint
{
      public:
      void setxy(int a,int b);
      void Move(int,int);
      void print();
      int getx();
      int gety();
      //protected:
      int x;
      int y;
};

void Tpoint::setxy(int a,int b)
{
      x=a;
      y=b;
}

void Tpoint::Move(int a,int b)
{
      x=x+a;
      y=y+b;
}

void Tpoint::print()
{
      cout<<x<<"    "<<y<<endl;
}

int Tpoint::getx()
{
      return x;
}

int Tpoint::gety()
{
      return y;
}

int main()
{
      Tpoint t;
      t.x=1;
      t.y=1;
      t.print();
      t.Move(t.x,t.y);
      t.print();
      t.Move(t.x,t.y);
      t.print();
      return 0;
}

在类体外定义成员函数的一般格式为

<函数类型> <类名> ::<成员函数> (<参数表>)

{

<函数体>

}

对象的定义及其对成员的访问

定义格式      <类名>  <对象名称>

访问      <对象名>.<成员名>     <对象名>.<成员函数名>(<参数表>)

还有就是指针的访问

用  ->

对象的初始化

构造函数和析构函数

#include <iostream>
using namespace std;

class Point
{
      public:
      Point(int a,int b)//构造函数
      {
            x=a;
            y=b;
      }

      ~Point()//析构函数
      {
            cout<<"析构函数!"<<endl;
      }

      private:
      int x,y;

};

int main()

{
      Point p(1,2);
      return 0;
}

复制构造函数

<类名>::<复制构造函数名>(<类名>&<引用名>)

{

<函数体>

}

#include <iostream>
using namespace std;

class Point
{
      private:
      int x,y;
      public:
      Point(int a,int b)
      {
            x=a;
            y=b;
      }

      Point(Point &p)
      {
            x=p.x;
            y=p.y;
      }

      void print()
      {
            cout<<x<<"   "<<y<<endl;
      }

      ~Point()
      {
            cout<<"析构函数!"<<endl;
      }

};

int main()
{
      Point p(1,1);
      p.print();
      Point P(p);//复制构造函数的妙处是可以把一个对象直接传入使得完成另一个对象的初始化
      P.print();
      return 0;

}

成员函数的特性

内联函数和外联函数:

内联函数提高运行速度

使用内联函数是需要注意的是:

  1. .在内敛函数中不允许使用循环语句和开关语句
  2. 内敛函数的定义必须出现在内联函数第一次被调用之前
  3. 内敛函数无法进行递归调用
#include <iostream>
using namespace std;

class  A
{
      private:
      int x,y;
      public:
      A(int a,int b)
      {
            x=a;
            y=b;
      }//内联函数   定义在类体内

      int getx()
      {
            return x;
      }//内联函数   定义在类体内

      int gety()
      {
            return y;
      }//内联函数   定义在类体内

      int sum();//内联函数   定义在类体外   但进行了转化inline

};

inline int A::sum()
{
      return getx()+gety();
}

int main()
{
      A a(1,2);
      cout<<a.sum()<<endl;
      return 0;

}

成员函数重载

除了析构函数不能重载外    其他的成语函数都可以重载   但是重载必须满足以下条件之一:

参数个数不同

参数类型不同

当参数个数相同时    参数类型至少有一个不同

仅有返回值不同的函数是不能重载的

//成员函数重载实例

#include <iostream>
using namespace std;

class B
{
      public:
      B(int a)
      {
            x=a+1;
            y=a;
      }

      B(int a,int b)
      {
            x=a;
            y=b;
      }

      int add();
      int add(int a);
      int add(int a,int b);

      int getx()
      {
            return x;
      }

      int gety()
      {
            return y;
      }

      private:
      int x,y;

};

int B::add()
{
      return x+y;
}

int B::add(int a)
{
      x=y=a;
      return x+y;
}

int B::add(int a,int b)
{
      x=a,y=b;
      return x+y;
}

int main()
{
      B m(5,45),n(6);
      cout<<m.getx()<<"  "<<m.gety()<<endl;
      cout<<n.getx()<<"  "<<n.gety()<<endl;
      int i=m.add();
      int j=m.add(1,2);
      cout<<i<<"   "<<j<<endl;
}

设置参数的默认值

#include <iostream>
using namespace std;

class B
{
      public:
      B(int a=10,int b=20,int c=30);
      int getx()
      {
            return x;
      }

      int gety()
      {
            return y;
      }

      int getz()
      {
            return z;
      }

      private:
      int x,y,z;
};

B::B(int a,int b,int c)//在这儿不能写成     B::B(int a=10,int b=20,int c=30)   切记
{
      x=a;y=b;z=c;
}

int main()
{

      B i,j(1),k(1,2),r(1,2,3);
      cout<<i.getx()<<"\t"<<i.gety()<<"\t"<<i.getz()<<endl;
      //j,k,r 同样的道理
      return 0;

}

 

静态成员(节省内存)

静态数据成员不属于对象,只属于类,不随对象的建立而建立,也不随对象的撤销而释放内存空间。自己独立开辟一块空间,为每个类对象所共同拥有。

#include <iostream>
using namespace std;

class _num
{
      public:
      int n;
      static int m;
};

int _num::m=10;//静态成员赋初值应该是这样的

int main()
{
      _num i,j;
      cout<<i.m<<endl;
}

静态成员函数的初始化应在类体外进行,格式如下:

<数据类型> <类名>::<静态数据成员名>=<初值>

在程序中如果静态数据成员的访问权限为(public)公有成员:

<类名>::<静态成员名>//前面直接用类名

静态成员被存放在静态存储区

静态成员函数

静态成员函数的引用

类名.静态成员函数(参数表);有时,也可以用对象:对象.静态成员函数(参数表)

#include <iostream>
using namespace std;

class test
{
      public:
      test(int a)
      {
            A=a;
            B+=a;
      }

      static void f(test m);//静态成员函数的声明
      private:
      int A;
      static int B;//静态私有成员  只能在类的体外进行初始化
};

int test::B=0;//初始化

void test::f(test m)
{
      cout<<m.A<<endl;
      cout<<B<<endl;
}

int main()
{
      test X(10),Y(20);
      test::f(X);
      Y.f(Y);//静态成员函数的访问
      return 0;
}

友元函数

友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的效率,但所矛盾的是,即使最大限度的保护,同样也破坏了类的封装特性,这是友元函数的缺点。

私有成员也能访问

友元类的定义格式:friend class <类名>

类的作用域与对象的生存期

类的嵌套

局部对象

静态对象

全局对象

类和对象的应用

类和指针

指向类对象的指针变量一般格式:<类名> *<对象指针名>

#include <iostream>
using namespace std;

class Time
{
      public:
      int hour;
      int minute;
      int sec;
      void get_time();
};

void Time::get_time()
{
      cout<<hour<<":"<<minute<<":"<<sec<<endl;
}

int main()
{
      Time *pt;
      Time t1,pt1;
      pt=&t1;
      pt1.hour=10;
      pt1.minute=10;
      pt1.sec=10;
      pt1.get_time();
      pt->hour=10;
      pt->minute=10;
      pt->sec=10;
      pt->get_time();
      return 0;
}

(*pt).hour或pt->hour
(*pt).get_time()或pt->get_time()

指向类的成员的指针

指向数据成员的指针变量的一般形式:<数据类型名> <类名>::*<指针变量名>

#include <iostream>
using namespace std;

class A
{
      public:
      int fun();
      int a;
};

int main()
{
      int A::*p;
      p=&A::a;
      cout<<"请输入一个数:"<<endl;
      A num;
      cin>>num.a;
      cout<<num.a<<endl;
      cout<<num.*p<<endl;
      return 0;
}

指向成员函数的指针格式如下:<数据类型> (类名::*指针变量名)(参数表);

为成员函数指针赋值的一般格式:指针变量名=&类名::成员函数名

#include <iostream>
using namespace std;

class A
{
      public:
      int a;
      int get_num()
      {
            cout<<"函数的指针指向"<<endl;
      }

};

int main()
{
      int (A::*p)();
      p=&A::get_num;
      return 0;
}

this指针

This指针是类自己的指针,不用声明和创建,对象的封装在编译器里面并不是真正的封装,实际上多个对象的成员函数在内存里只有一份,而对象的成员变量是不同的,为了辨别是哪个对象的调用,在调用成员函数时,编译器隐含的插入一个参数,这个参数就是this指针,当一个对象调用成员函数时,系统先将该对象的地址赋给this指针,并将该指针作为一个变量自动传递给该成员函数,即在成员函数里指向对象自己。

//this 指针的使用
#include <iostream>
using namespace std;

class A
{
      public:
      A(int x,int y);
      void print()
      {
            cout<<a<<'\t'<<this->b<<endl;
      }

      private:
      int a,b;
};

A::A(int x,int y)
{
      a=x;
      this->b=y;
}

int main()
{
      A m(1,2),n(10,20);
      m.print();
      n.print();
      return 0;

}

类和数组

<类名> <数组名>[<数组大小>]

对象指针数组

<类名>  *<数组名>[<数组大小>]

#include <iostream>
using namespace std;

class A
{
      public:
      A()
      {
            x=y=0;
      }

      A(int a,int b)
      {
            x=a;
            y=b;
      }

      int getx()
      {
            return x;
      }

      int gety()
      {
            return y;
      }

      private:
      int x,y;

};

int main()
{
      A p1(1,2),p2(3,4),p3(5,6),p4(7,8);
      A *p[4]={&p1,&p2,&p3,&p4};
      for(int i=0;i<4;i++)
      {
            cout<<p[i]->getx()<<","<<p[i]->gety()<<endl;
      }

      return 0;
}

指向对象数组的指针

<类名>  (*<数组名>)[<数组大小>]

#include <iostream>
using namespace std;

class A
{
      public:
      A()
      {
            x=y=0;
      }

      A(int a,int b)
      {
            x=a;
            y=b;
      }

      int getx()
      {
            return x;
      }

      int gety()
      {
            return y;
      }

      private:
      int x,y;
};

int main()
{
      A p1[2][3]={A(1,2),A(3,4),A(5,6),A(7,8),A(9,10),A(11,12)};
      //A p1[2][3]={{A(1,2),A(3,4),A(5,6)},{A(7,8),A(9,10),A(11,12)}};
      A (*p)[3]=p1;
      for(int i=0;i<2;i++)
      {
            for(int j=0;j<3;j++)
            {
                  cout<<(*(p+i)+j)->getx()<<","<<(*(p+i)+j)->gety()<<"\t";
                  //cout<<(*(*(p+i)+j)).getx()<<","<<(*(*(p+i)+j)).gety()<<"\t\t"; 
            }

            cout<<endl;
      }

      return 0;
}

常类型

常对象:在对象名前使用const修饰的对象

定义格式如下:

<类名> const <对象名>(<初值>)

或者

const  <类名> <对象名>(<初值>)

常成员函数和常数据成员

格式:

<类型说明符> <函数名>(<参数表>) const

#include <iostream>

using namespace std;

class A
{

      public:
      A(int a,int b,int c):z(c)
      {
            x=a;y=b;
      }

      //A(int a,int b,int c):x(a),y(b),z(c){}
      void print()
      {
            cout<<x<<"   "<<y<<"   "<<z<<endl;
      }

      void print() const
      {
            cout<<x<<"   "<<y<<"   "<<z<<endl;
      }

      private:
      int x,y,z;
};

int main()
{
      A a(5,4,3);
      a.print();
      const A b(15,25,35);
      b.print();

      return 0;
}

子对象与堆对象

子对象:当一个类的成员是另一个类的对象时,该对象就是子对象

堆对象:在程序运行过程中根据需要随时建立或删除的对象

New 运算符使用格式如下:  new <类型说明符> (<初始化列表>)

A *p1,*p2;

      p1=new A(1,2);

      delete p1;

      p2=new A[5];

      delete []p2;

#include <iostream>
using namespace std;

class Tdate
{
      public:
      Tdate(int y,int m,int d)
      {
            year=y;
            month=m;
            day=d;
            cout<<"构造函数!"<<endl;
      }

      Tdate()
      {
            cout<<"缺省构造函数!"<<endl;
      }

      ~Tdate()
      {
            cout<<"析构函数!"<<endl;
      }

      void show();
      private:
      int year,month,day;
};

void Tdate::show()
{
      cout<<year<<"."<<month<<"."<<day<<endl;
}

int main()
{
      Tdate *a,*p;
      //Tdate s;
      a=new Tdate(2008,12,31);//创建无名对象   调用构造函数
      a->show();//显示a指向的无名对象值
      //delete a;
      p=new Tdate[2];//创建两个元素的无名对象数组,p指向该数组
      p[0]=Tdate(2008,12,31);
      p[1]=Tdate(2008,12,31);
      for(int i=0;i<2;i++)
      {
            p[i].show();
      }
      delete []p;

      return 0;
}

继承性和派生类

基类和派生类

子类能够继承父类的全部特征,包括所有的数据成员和成员函数;

派生类的定义格式:

单继承定义格式:

Class <派生类名>:<继承方式> <基类名>

{

<继承类新定义的成员>

}

多继承的定义格式:

Class <派生类名>:<继承方式1> <基类名1>,<继承方式2> <基类名2>,.............

{

<继承类新定义的成员>

}

继承方式有三种:

Public protected private

在缺省默认情况下是   private

       

基类   

公有成员

私有成员   

 

保护成员

公有派生类

公有成员

不可访问成员

保护成员

私有派生类  

私有成员

不可访问成员

私有成员

保护派生类

保护成员

不可访问成员

保护成员

#include <iostream>
using namespace std;

class A
{
      private:
      int s;
      public:
      void initt(int n)
      {
            s=n;
      }

      int gets()
      {
            return s;
      }
};

class B:public A
{
      private:
      int t;
      public:
      void initt(int n)
      {
            t=n;
      }

      int gett()
      {
            return t*gets();
      }
};

int main()
{
      B ob;
      ob.A::initt(8);//注意访问的形式
      ob.initt(8);
      cout<<ob.gett()<<endl;

      return 0;
}         

还有就是私有继承的时候   会是和这个不一样的  在私有继承中还是和类的私有成员的访问差不多的。

单继承派生类的构造函数和析构函数

对于构造函数,先执行基类的,在执行对象成员的,最后执行派生类的。

对于析构函数,先执行派生类的,在执行对象成员的,最后执行基类的。

派生类构造函数声明的一般格式为:

<派生类名> (参数总表):基类名(参数表),对象成员名1(参数表1)...

{

//派生类新增成员的初始化语句

}

注意:读程序

多继承中出现的二义性问题解决方法:

使用作用域运算符::

使用同名覆盖的原则

使用虚函数virtual

多重继承的声明

Class <派生类名>:<继承方式1> <基类名>,...,<继承方式n> <基类名n>

{

派生类成员声明;

};

多继承的构造函数和析构函数:

构造函数声明:

<派生类名>(参数总表):基类名1(参数表1),...,基类名n(参数表n),对象成员名1(对象成员参数表1),...,对象成员名m(对象成员参数表m)

{

//派生类新增成员的初始化语句

}

虚基类的作用

防止二义性  对于多继承而言

虚基类的定义:

Class <派生类名>:virtual <继承方式> <基类名>

虚基类的构造函数及其初始化:

虚基类的构造函数的执行在非虚基类的构造函数之前

若同一层次中包含多个虚基类,这些虚基类的构造函数按照对他们说明的先后次序执行

若虚基类由非虚基类派生而来,则任然先执行基类的构造函数,在执行派生类的构造函数

 

多态性和虚函数

函数重载

所谓函数重载就是给同一个函数名多个含义,构造函数的重载给初始化带来了更大的灵活性。

运算符重载

不能重载的运算符有:

.   .*   ->*   ::   ?:          这五个运算符是不可以重载的

注意:除了new和delete这两个运算符之外,任何运算符如果作为成员函数重载时不得重载为静态函数,=    []   ()   ->以及所有的类型转换运算符只能作为成员函数重载,而且不能使针对枚举类型操作数的重载。

运算符重载时要遵循四个不变:
不能改变原运算符的优先级

不能改变原运算符的结合性

不能改变元运算符的操作个数

不能改变原运算符的语法语义

运算符函数的函数名是由运算符前加关键字operator构成的。

运算符“+”的重载(虚数)

#include <iostream>
using namespace std;

class fushulei
{
      public:
      int shibu;
      int xubu;
      //public:
      fushulei(int sb=0,int xb=0)
      {
            shibu=sb;
            xubu=xb;
      }

      fushulei operator+ (fushulei &s )
      {
            fushulei p;
            p.shibu=s.shibu+ shibu;
            p.xubu=s.xubu+ xubu;

            return p;
      }

};

int main()
{
      fushulei s(1,1),r(2,2),p;
      p=s+r;
      cout<<p.shibu<<"+"<<p.xubu<<"i"<<endl;

      return 0;
}

前缀 ++重载

Friend fraction &operator ++(fraction &f)

{

F.num+=f.den;

Return f;

}

后缀  ++重载

Friend fraction operator ++(fraction &f,int)               ///后面的int必须有

{

F.num+=f.den;

Return fraction(f.num  - f.den,f.den);

}

也可以类似的描述,上面两个只是例子,实际之中还得考虑怎么用

<<    >>的重载

friend istream &operator >>(istream &i,lei &f)

{

      i>>f.s>>f.x;

      return i;

}

cin>>f;

friend ostream &operator >>(ostream &o,lei &f)

{

      I<<f.s<<f.x;

      return o;

}

Cout<<f<<endl;

[]的重载

虚函数

Class 类名

{

Virtual 类型 函数名(参数表);

}

#include <iostream>
using namespace std;

class mianji
{
      public:
      mianji()
      {
      }

      virtual double area()
      {
            return 0;
      }
};

class juxing:public mianji
{
      private:
      int chang;
      int kuan;
      public:
      juxing(int a,int b)
      {
            chang=a;
            kuan=b;
      }

      virtual double area()
      {
            return chang*kuan;
      }
};

int main()
{
      mianji mj;
      juxing jx(2,3);
      cout<<mj.area()<<endl;
      cout<<jx.area()<<endl;

      return 0;
}

纯虚函数和抽象类

纯虚函数是一种特殊的虚函数,在基类中只给出说明,而在派生类中给出定义的虚函数成为纯虚函数,出虚函数在基类中的一般说明格式:

Class 类名

{

Virtual 类型 函数名(参数表)=0;

};

#include <iostream>
using namespace std;

class mianji
{
      public:
      virtual double area()=0;
};

class juxing:public mianji
{
      private:
      int chang;
      int kuan;
      public:
      juxing(int a,int b)
      {
            chang=a;
            kuan=b;
      }

      virtual double area()
      {
            return chang*kuan;
      }

};

int main()
{
      juxing jx(2,3);
      cout<<jx.area()<<endl;

      return 0;
}

只要是在结构体或者是类中的对对象的指针要先申请内存    用new和delete

包含纯虚函数的类叫做抽象类,抽象类必须用作派生其他类的基类,而不能直接创建对象实例,但可以使用抽象类的指针。

 

虚析构函数:
      为了能够使程序执行时,内存的空间及时得到释放,将析构函数可以定义为虚析构函数。声明虚析构函数时,只需在析构函数前加关键字virtual即可。

说明析构函数的目的在于在使用delete运算符删除一个对象时,能确保析构函数被正确的执行,因为设置析构函数后,可以采用动态联编方式选择析构函数。

 

C++的IO流类库

”<<”插入运算符,向输出流中插入一个字符序列,重载在ostream类中定义,cout是数据的目的地。其功能在于数据输出,其原型有:

ostream &operator <<(ostream &<类型修饰符>)

put()成员函数

Cout.put(cahr c);

Cout.put(const char c);

write()成员函数

成员函数write()也可以实现屏幕输出。使用成员函数write()输出一个字符串,格式:

Cout.write(const char *str,int  n) 

#include <iostream>

using namespace std;

int main()

{

      cout.write("千里之行,始于足下。运筹帷幄,决胜千里!\n",41);

      return 0;

}

标准输入

“>>”为提取运算符,从输入流中提取一个字符序列,重载在istream类中定义,最一般的键盘输入是将提取符作用在流类的对象cin上,格式:

Cin>><表达式>>><表达式>...

get()成员函数

Cin.get(char &ch);

#include <iostream>
using namespace std;

int main()

{
      char c;
      cin.get(c);
      cout.put(c).put('\n');

      return 0;
}

读取多个字符:

Cin.getline(char *buf,int limit,Deline=’\n’);

例题:编程统计从键盘上输入一行字符的个数,从中选取最短的行的字符个数,统计共输入多少行。

#include <iostream>
using namespace std;

const int SIZE=80;

int main()
{
      int lcnt=0,lmin=32767;
      char buf[SIZE];
      cout<<"输入字符:"<<endl;
      while(cin.getline(buf,SIZE))
      {
            int count=cin.gcount();
            lcnt++;
            if(count<lmin) lmin=count;
            cout<<"Line #"<<lcnt<<"\t"<<"chars read:"<<count<<endl;
            cout.write(buf,count).put('\n').put('\n');
      }

      cout<<endl;
      cout<<"Total line:"<<lcnt<<endl;
      cout<<"shortest line:"<<lmin<<endl;

      return 0;
}

//结束使用ctrl+z

read成员函数

成员函数read()可以从输入流中读取指定数目的字符,并将它们存入到指定的数组中,该函数使用格式如下:

Cin.read(char *buf,int size)

>>   <<运算符的重载

Istream &operator >>(istream &i,Pint &p)........................return i;

Ostrstream类的构造函数和istrstream类的构造函数

#include <iostream.h>

#include <fstream.h>

#include <strstrea.h>

Char buf[N];

Ostrstream out1(buf,sizeof(buf));

Out1<<.........................

 

#include <iostream.h>

#include <strstrea.h>

Char buf[]={..................}

Istrstream ss(buf);

Ss>>................

磁盘文件的打开和关闭

文件流类  对象名

例子:

Fstream outline;

Outfile.open(“d:\\sav\\file.txt”,ios::out);

#include <iostream.h>
#include <fstream.h>

int main()
{

   fstream outline;
   outline.open("G:\\文件.txt",ios::out);
   outline.write("千里之行始于足下\n",20);
   outline.close();

   return 0;
}

模板

模板是一种工具,它是C++语言支持参数化多态性的工具。模板是用来解决代码重用的一种方法。重用代码要求有通用性,即不受使用的数据类型的影响。模板分为函数模板和类模板两种 。

函数模板

Template <(参数化类型名表)>

<类型> <函数名>(<模板参数表>)

{

<函数体>

}

参数化类型名表又称模板参数表,多个表项用逗号分隔。每个表项称为一个模板参数(模板形参)。格式如下:

Class <参数表>

Typename <参数名>

<类型修饰> <参数表>

Class是修饰符,用来表示其后面的标识符是参数化的类型名,即模板参数。这三种格式中,前两种是等价的,关键字typename与class可以互换,用typename和class声明的参数成为虚拟类型参数;而用<类型修饰符>声明的参数则称为常规参数,在形式上与普通的函数参数声明相同

不但普通函数可以声明为函数模板,类的成员函数也可以声明为模板函数。

#include <iostream>
using namespace std;
#include <string.h>

template <class T> void bubble(T *item,int count);

int main()
{

   int nums1[]={4,7,2,9,3,7,6,1};
   bubble(nums1,8);
//T   就像在此处的 double nums2[]={.......}的时候    还是照//样使用bubble(nums1,8);这个函数的
//这样就起到了模板的作用
   cout<<"The sorted numbers are";
   for(int i(0);i<8;i++)
   {
      cout<<nums1[i]<<"  ";
   }
   cout<<endl;
   return 0;
}

//template <class T> //对于模板来说 这个T代表了所有的例如int //float  double........等很多的变量类型

//在这儿T就是一个和int等相似的类型说明

void bubble(T *item,int count)
{
   T t;
   for(int i=0;i<count-1;i++)
   {
      for(int j=0;j<count-1;j++)
      {
         if(item[i]>item[j])
         {
            t=item[i];
            item[i]=item[j];
            item[j]=t;
         }
      }
   }

}//这是冒泡排序

模板实参的省略

模板实参省略是有条件的:

以下四种情况模板实参不能省略:

从模板函数实参表获得的信息有矛盾

例如:

Template <typename T>

T add(T a,T b)

{return a+b;}

若调用函数的语句为:

Cout<<add(3,5);

则编译系统无论是从3,还是5所获得的信息都是T对应的int,因此可顺利的生成如下模板函数:

Int add(int a,int b)

{return a+b;}

若调用函数的语句为:

Cout<<add(3.0,5);

则编译从3.0所获得的信息T对应于double,而从5所获得的信息是T所对应于int,两者是矛盾的,为了解决这一问题,可以显式的给出模板实参,强制生成对特定实例的调用,必要时生成该实例。调用语句变为:

Cout<<add<int>(3.0,5);

其中紧跟在函数名后的<int>就是模板实参表,此时,编译系统生成如下的模板函数:

Int add(int a,int b)

{return a+b;}

需要获得特定类型的返回值,而不管参数的类型如何

例如:

如果需要add返回一个int型的值,那么直接以add<int>(a,b);形式调用即可。

虚拟类型参数没有出现在模板函数的形参表中

由于虚拟参数类型参数没有出现在模板函数的形参表中,所以调用时不可能从模板函数的实参表中获得相应的信息,因此无法省略。

#include <iostream>
using namespace std;

template <class T1,class T2,class T3>

T2 add(T1 a,T3 b)//函数返回类型是T2
{
   return a+b;
}

int main()
{
   cout<<showpoint;
   cout<<add<double,int>(3,5l)<<endl;
   cout<<add<int,double,long>(3,5l)<<endl;
   return 0;
}

函数模板含有常规形参

当函数模板含有常规形参时,如果常规参数的信息无法从模板函数的实参表中获得,则在调用时必须显式地给出对应常规参数的模板实参。

#include <iostream>
using namespace std;

template <class T,int rows>

void sum(T data[],T &result)
{

   result=0;
   for(int i=0;i<rows;i++)
   {
      result+=data[i];
   }
}

int main()
{
   int d[3]={1,2,3};
   int r;
   sum<int,3>(d,r);//这是一种独特的用法   记住*
   cout<<"sum="<<r<<endl;

   return 0;
}

类模板

类模板的概念与定义

使用类模板所定义的一种类类型,类中的某些数据成员和某些成员函数的参数及返回值可以选取一定范围内的多种类型,从而实现代码重用。类模板就是一系列相关类的模型或样板。这些类的成员组成相同,成员函数的源代码形式相同,所不同的只是所针对的类型(成员的类型以及成员函数的参数和返回值类型)。对于类模板数据类型本身成了它的参数,因而是一种参数化类型的类,是类的生成器,类模板中声明的类成为模板类。

声明一个类模板的格式是:

Template <模板参数表>

Class <类名>

{

<类体说明>

}

<模板参数表>是由一个或多个模板形参组成,它与一般的类声明不同之处在于,这里的类要用<模板参数表>中虚拟类型参数来修饰它的某些成员,使模板类独立于任何具体的数据类型。

在模板类外对成员函数的声明格式是:

Template <模板参数表>

<返回类型> <类名> <<模板参数表>>:<函数名>(<函数参数表>)<函数体>

 

类模板的成员函数都是模板函数,用类模板定义对象的格式是:

<类名> <<模板实参表>>  <对象名>(<构造函数实参表>)

 

模板类

模板来是一个类型参数化的样本,是一组模板类的集合。模板类是某个类模板的实例。使用某种类型类替换某个类模板的模板参数可生成该类模板的一个模板类。使用模板类可以定义该类的对象,进而实现所需要的操作。

#include <iostream.h>
#include <stdlib.h>

template <class T>

class stack
{
   public:
   stack(int size);
   ~stack()
   {
      delete []stck;
   }

   void push(T i);
   T pop();
   private:
   int tos,length;
   T *stck;
};

template <class T>

stack<T>::stack(int size)
{
   stck=new T[size];
   if(!stck)
   {
      cout<<"cannot allocate stack.\n";
      exit();
   }

   length=size;                                //栈长度
   tos=0;

}

template <class T>

void stack<T>::push(T i)                         //入栈
{
   if(tos==length)                                //满栈
      cout<<"Stack is full.\n";
   stck[tos]=i;
   tos++;
}

template <class T>

T stack<T>::pop()                                 //出栈
{
   if(tos==0)                              
      cout<<"Stack underflow.\n";
      tos--;
      return stck[tos];
}

int main()
{
   stack<int>a(10);
   stack<double>b(10);
   stack<char>c(10);
   for(int i=0;i<10;i++)
   {
      a.push(i);
   }

   for(int i=0;i<10;i++)
   {
      b.push(i*2.5);
   }

   for(int i=0;i<10;i++)
   {
      cout<<a.pop()<<' ';
   }

   cout<<endl;

   for(int i=0;i<10;i++)
   {
      cout<<b.pop()<<' ';
   }

   cout<<endl;

   for(int i=0;i<10;i++)
   {
      c.push((char)'j'-i);
   }

   for(int i=0;i<10;i++)
   {
      cout<<c.pop()<<' ';
   }

   cout<<endl;
 

   return 0; 
}

模板类的继承与派生

模板类的派生与普通类的一样,也分为公有派生类、保护派生类和私有派生类三种。模板派生类中成员的访问控制规则与普通类也是一样的。一个模板类可以作为一个普通类的派生类,也可以作为其他模板类的基础。下面给出常见的几种情况:

普通继承类模板

Template <class T>

Class TBase

{

T data;

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

};

Class adsd:public TBase<int>

{

..........

};

模板类继承普通类

Class TBase

{

........

};

Template <class T>

Class adsd:public TBase

{

Tdata;

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

}

模板类继承模板类

Template <class T>

Class TBase

{

T data1;

.........

};

Template <class T1,class T2>

Class sadsd:public TBase<T1>

{

T2 data2;

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

};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值