C++入门学习实例

来源:互联网 发布:大数据服务 编辑:程序博客网 时间:2024/06/17 08:25

C++面向对象:

  类,是创建对象的模板,一个类可以创建多个相同的对象;对象,是类的实例,是按照类的规则创建的。
  类是抽象的,不占用内存,而对象是具体的,占用存储空间。
  用struct声明的结构体类型实际上也就是类。用struct声明的类,如果对其成员不作private或public的声明,系统将其默认为public。而用class定义的类,如果不作private或public声明,系统将其成员默认为private,在需要时也可以自己用显式声明改变。如果希望成员是公用的,使用struct比较方便,如果希望部分成员是私有的,宜用class。

#include "stdafx.h"#include <iostream>using namespace  std;   // 使用命名空间class People{public:     void say(){        cout<<"Hello"<<endl;    }};                          //类的声明必须以分号结束!int main(){    People *p=new People();//指针对象    p->say();    delete p;    People pel;        //值对象    pel.say();    People *q=&pel; //指向对象的指针    q->say();    return 0;}


C++构造函数和析构函数

创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。
注意,类的数据成员是不能在声明类时初始化的。如果一个类中所有的成员都是公用的,则可以在定义对象时对数据成员进行初始化。如:

class Time{   public : //声明为公用成员   hour;   minute;   sec;};Time t1={14,56,30};  //将t1初始化为14:56:30

  这种情况和结构体变量的初始化是差不多的,在一个花括号内顺序列出各公用数据成员的值,两个值之间用逗号分隔。但是,如果数据成员是私有的,或者类中有private或protected的成员,就不能用这种方法初始化。
  
  为了解决这个问题,C++提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。
  构造函数的名字必须与类名同名,而不能由用户任意命名,以便编译系统能识别它并把它作为构造函数处理。它不具有任何类型,不返回任何值。构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数。

  析构函数(destructor)也是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号。当对象的生命期结束时,会自动执行析构函数。
  注意:析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。

#include "stdafx.h"#include <iostream>#include <string>using namespace std;class People{private:    int i;public:    People(int i){        this->i=i;    cout<<"构造函数被执行"<<i<<endl;    }    ~People(){    cout<<"析构函数被执行"<<i<<endl;    }    void say(){    cout<<"Hello,I am student"<<i<<endl;    }};int main(){    //People *p = new  People();          //创建指针对象时构造函数被执行    //delete p;                            //删除对象时析构函数被执行    People p1(1);                          //创建值对象,自动调用构造函数和析构函数    p1.say();    People p2(2);     p2.say();    return 0;};

拷贝构造函数

  拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。

拷贝构造函数的形式:
Class X
{
public:
  X();
  X(const X &a);//拷贝构造函数
}

拷贝构造函数调用的三种形式:
1.一个对象作为函数参数,以值传递的方式传入函数体;
2.一个对象作为函数返回值,以值传递的方式从函数返回;
3.一个对象用于给另外一个对象进行初始化(常称为复制初始化)。

#include "StdAfx.h"#include <iostream>using namespace std;class B{private:  int j;public:    B() {setI(1);}    B(int k) {setI(k);}  //函数重载    ~B(){}    B(B &a)   {        j=a.j;        cout<<"拷贝构造被调用"<<endl;    }    int setI(int j){return this->j=j;}    int getI(){return j;}};void fun1(B b){    cout<<"fun1输出:"<<b.getI()<<endl;}B fun2(){    B b(3);    return b;}int main( ){B  b1(3);   //创建第一个对象b1B  b2(b1);   //b1初始化b2,第一次调用拷贝构造函数fun1(b2);   //对象b2作为fun1实参,第二次调用拷贝构造函数fun2();    //函数返回值是类对象,第三次调用拷贝构造函数return 0; }


C++类的继承与派生

  在C++中,所谓“继承”就是在一个已存在的类的基础上建立一个新的类。已存在的类称为“基类(base class)”或“父类(father class)”,新建的类称为“派生类(derived class)”或“子类(son class )”。
  一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从另一角度说,从已有的类(父类)产生一个新的子类,称为类的派生。
  一个派生类有两个或多个基类的称为多重继承(multiple inheritance)。

  声明派生类的一般形式为:
class 派生类名:[继承方式] 基类名
  {
  派生类新增加的成员
   };

  
  继承方式包括public(公用的)、private(私有的)和protected(受保护的),如果不写此项,则默认为private(私有的)。
  1) 公用继承(public inheritance):
  基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。
  2) 私有继承(private inheritance):
  基类的公用成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。
  3) 受保护的继承(protected inheritance):
  基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有。保护成员的意思是,不能被外界引用,但可以被派生类的成员引用。

#include "stdafx.h"#include <iostream>#include <string>using namespace std;class People{    private:           string name;    public:         void getName(string name){            this->name=name;            cout<<"Name:"<<name<<endl;        }};class Student:public People{ //公有继承    private:        int age;    public:         void getAge(int age){            this->age=age;            cout<<"Age:"<<age<<endl;        }};int main(){    Student *p=new Student();    p->getName("Zhang");//只能通过基类的公用成员函数来引用基类的私有数据成员    p->getAge(12);    delete p;    return 0;}

  此外,在声明派生类时,一般还应当自己定义派生类的构造函数和析构函数,因为构造函数和析构函数是不能从基类继承的。

C++多态性与虚函数、纯虚函数、函数重写、抽象类

  虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。当把基类的某个成员函数声明为虚函数后,允许在其派生类中对该函数重新定义,赋予它新的功能,并且可以通过指向基类的指针指向同一类族中不同类的对象,从而调用其中的同名函数。

虚函数的使用方法是:
1. 在基类用virtual声明成员函数为虚函数。
2. 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。
3. 定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。
4. 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
  需要说明;有时在基类中定义的非虚函数会在派生类中被重新定义,如果用基类指针调用该成员函数,则系统会调用对象中基类部分的成员函数;如果用派生类指针调用该成员函数,则系统会调用派生类对象中的成员函数,这并不是多态性行为(使用的是不同类型的指针),没有用到虚函数的功能。

#include "stdafx.h"#include <iostream>#include <string>using namespace std;class People{    public:         virtual void say(){     //定义为虚函数        cout<<"People say."<<endl;        }};class Student:public People{     public:             void say(){            //函数重写        cout<<"Students say."<<endl;        }};int main(){    People p;    Student s;     People *pt=&p;//定义指向基类对象的指针变量pt;     pt->say();     pt=&s;        //可调用派生类中的同名函数;若之前无virtual关键字,执行基类方法     pt->say();    return 0;}



  
  纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是
   
    virtual 函数类型 函数名 (参数表列) = 0;

关于纯虚函数需要注意的几点:
1. 纯虚函数没有函数体;
2. 最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”;
3. 这是一个声明语句,最后应有分号。

  纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。
  纯虚函数只有函数的名字而不具备函数的功能,不能被调用。它只是通知编译系统:“在这里声明一个虚函数,留待派生类中定义”。因为纯虚函数是不能被调用的,包含纯虚函数的类是无法建立对象的。在派生类中对此函数提供定义后,它才能具备函数的功能,可被调用。
  如果在基类中没有保留函数名字,则无法实现多态性。如果在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义,则该虚函数在派生类中仍然为纯虚函数

  凡是包含纯虚函数的类都是抽象类这种不用来定义对象而只作为一种基本类型用作继承的类,称为抽象类(abstract class ),由于它常用作基类,通常称为抽象基类(abstract base class )。
  抽象类的作用是作为一个类族的共同基类,或者说,为一个类族提供一个公共接口。一个类层次结构中当然也可不包含任何抽象类,每一层次的类都是实际可用的,可以用来建立对象的。

#include "stdafx.h"#include <iostream>#include <string>using namespace std;class People{   //抽象类    public:         virtual void say()const=0;    //定义为纯虚函数};class Student:public People{     public:              void say()const{             //对纯虚函数进行再定义        cout<<"Students say."<<endl;        }};int main(){    Student s;    s.say();   ////静态关联    People *pt;  //定义基类指针    pt=&s;   //指针指向Student类对象    pt->say();  //动态关联    return 0;}

C++运算符重载

C++中运行时的多态性主要是通过虚函数来实现的,而编译时的多态性是由函数重载和运算符重载来实现的。
运算符重载规则如下:
1, C++中的运算符除了少数几个之外,全部可以重载,而且只能重载C++中已有的运算符。
2, 重载之后运算符的优先级和结合性都不会改变。
3, 运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。一般来说,重载的功能应当与原有功能相类似,不能改变原运算符的操作对象个数,同时至少要有一个操作对象是自定义类型。
4,不能重载的运算符只有五个,它们是:成员运算符“.”、指针运算符“*”、作用域运算符“::”、“sizeof”、条件运算符“?:”。
5,运算符重载形式有两种,重载为类的成员函数重载为类的友元函数
6,运算符重载为类的成员函数的一般语法形式为:
  函数类型 operator 运算符(形参表)
  {
   函数体;
  }
运算符重载为类的友元函数的一般语法形式为:
  friend 函数类型 operator 运算符(形参表)
  {
   函数体;
  }
  其中,函数类型就是运算结果类型;operator是定义运算符重载函数的关键字;运算符是重载的运算符名称。
  当运算符重载为类的成员函数时,函数的参数个数比原来的操作个数要少一个;当重载为类的友元函数时,参数个数与原操作数个数相同。原因是重载为类的成员函数时,如果某个对象使用重载了的成员函数,自身的数据可以直接访问,就不需要再放在参数表中进行传递,少了的操作数就是该对象本身。而重载为友元函数时,友元函数对某个对象的数据进行操作,就必须通过该对象的名称来进行,因此使用到的参数都要进行传递,操作数的个数就不会有变化。
  运算符重载的主要优点就是允许改变使用于系统内部的运算符的操作方式,以适应用户自定义类型的类似运算。

重载为类的成员函数:

#include "StdAfx.h"#include <iostream> using namespace std;class complex{public:complex() { real=imag=0; }       //构造函数初始化complex(double r, double i){     //有参构造函数重载real = r, imag = i;}complex operator +(const complex &c); //申明运算符重载complex operator *(const complex &c);friend void print(const complex &c);  //友元函数访问私有变量private:double real, imag;};inline complex complex::operator +(const complex &c)  //内联函数提高函数的执行效率{return complex(real + c.real, imag + c.imag);}inline complex complex::operator *(const complex &c){return complex(real * c.real - imag * c.imag, real * c.imag + imag * c.real);}void print(const complex &c){if(c.imag<0)cout<<c.real<<c.imag<<'i'<<endl;elsecout<<c.real<<'+'<<c.imag<<'i'<<endl;}void main(){complex c1(2.0, 3.0), c2(4.0, -2.0), c3;c3 = c1 + c2;cout<<"c1+c2=";print(c3);c3 = c1 * c2;cout<<"c1*c2=";print(c3);c3 = (c1+c2) * c1;cout<<"(c1+c2)*c1=";print(c3);cout<<endl;}

重载为类的友元函数:

#include "StdAfx.h"#include <iostream> using namespace std;class complex{public:complex() { real=imag=0; }complex(double r, double i){real = r, imag = i;}friend complex operator +(const complex &c1, const complex &c2);friend complex operator *(const complex &c1, const complex &c2);friend void print(const complex &c);private:double real, imag;};complex operator +(const complex &c1, const complex &c2){return complex(c1.real + c2.real, c1.imag + c2.imag);}complex operator *(const complex &c1, const complex &c2){return complex(c1.real * c2.real - c1.imag * c2.imag, c1.real * c2.imag + c1.imag * c2.real);}void print(const complex &c){if(c.imag<0)cout<<c.real<<c.imag<<'i'<<endl;elsecout<<c.real<<'+'<<c.imag<<'i'<<endl;}void main(){complex c1(2.0, 3.0), c2(4.0, -2.0), c3;c3 = c1 + c2;cout<<"c1+c2=";print(c3);c3 = c1 * c2;cout<<"c1*c2=";print(c3);c3 = (c1+c2) * c1;cout<<"(c1+c2)*c1=";print(c3);}

两种重载形式的比较
  一般说来,单目运算符最好被重载为成员;对双目运算符最好被重载为友元函数,双目运算符重载为友元函数比重载为成员函数更方便,但是,有的双目运算符还是重载为成员函数为好,例如,赋值运算符。因为,它如果被重载为友元函数,将会出现与赋值语义不一致的地方。

C++函数指针

  1. 定义
    每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针。
  2. 语法
    指向函数的指针变量的一般定义形式为:
     数据类型 (*指针变量名)(参数表);
  3. 说明
    1) 函数指针的定义形式中的数据类型是指函数的返回值的类型。
    2) 区分下面两个语句:
      int (*p)(int a, int b); //p是一个指向函数的指针变量,所指函数的返回值类型为整型
      int *p(int a, int b); //p是函数名,此函数的返回值类型为整型指针

    3) 指向函数的指针变量不是固定指向哪一个函数的,而只是表示定义了一个这样类型的变量,它是专门用来存放函数的入口地址的;在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。
    4) 在给函数指针变量赋值时,只需给出函数名,而不必给出参数。
    如函数max的原型为:int max(int x, int y); 指针p的定义为:int (*p)(int a, int b); 则p = max;的作用是将函数max的入口地址赋给指针变量p。这时,p就是指向函数max的指针变量,也就是p和max都指向函数的开头。
    5) 在一个程序中,指针变量p可以先后指向不同的函数,但一个函数不能赋给一个不一致的函数指针(即不能让一个函数指针指向与其类型不一致的函数)。
    如有如下的函数:int fn1(int x, int y); int fn2(int x);
    定义如下的函数指针:int (*p1)(int a, int b); int (*p2)(int a);

    p1 = fn1; //正确
    p2 = fn2; //正确
    p1 = fn2; //产生编译错误
    6) 定义了一个函数指针并让它指向了一个函数后,对函数的调用可以通过函数名调用,也可以通过函数指针调用(即用指向函数的指针变量调用)。如语句:
      c = (*p)(a, b); //表示调用由p指向的函数(max),实参为a,b,函数调用结束后得到的函数值赋给c
    7) 函数指针只能指向函数的入口处,而不可能指向函数中间的某一条指令。不能用*(p+1)来表示函数的下一条指令。
    8) 函数指针变量常用的用途之一是把指针作为参数传递到其他函数。
#include "StdAfx.h"#include <iostream>   #include <conio.h>  using namespace std; int max(int x, int y); //求最大数  int min(int x, int y); //求最小数  int add(int x, int y); //求和  void process(int i, int j, int (*p)(int a, int b)); //应用函数指针  int max(int x, int y){      return x > y ? x : y;  }    int min(int x, int y){      return x > y ? y : x;  }    int add(int x, int y){      return x + y;  }    void process(int i, int j, int (*p)(int a, int b)){      cout<<p(i, j)<<endl;  }  int main()  {      int x, y;      cin>>x>>y;      cout<<"Max is: ";      process(x, y, max);      cout<<"Min is: ";      process(x, y, min);      cout<<"Add is: ";      process(x, y, add);      return 0;  }  

C++引用

  引用的两个主要用途:作为函数参数以及从函数中返回左值
  引用引入了对象的一个同义词。定义引用的表示方法与定义指针相似,只是用&代替了*。例如:
Point pt1(10,10);
Point &pt2=pt1;   //定义了pt2为pt1的引用。通过这样的定义,pt1和pt2表示同一对象。
  需要特别强调的是引用并不产生对象的副本,仅仅是对象的同义词。因此,当下面的语句执行后:
pt1.offset(2,2);  pt1和pt2都具有(12,12)的值。
  引用必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义一个引用后才初始化它。例如下面语句是非法的:
  Point &pt3;
  pt3=pt1;

  引用和指针的比较:
(1)引用总是指向某个对象:定义引用时没有初始化是错误的。而指针在定义时则可以不进行初始化;
(2)赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。而给指针赋值,则修改指针本身的值;
(3)引用一经初始化,就始终指向同一个特定对象,不能修改。而指针在定义后则可以修改;
(4)不存在指向空值的引用。而存在指向空值的指针。因此使用引用的代码效率比使用指针的要高,因为在使用引用之前不需要测试它的合法性。

  总体来说,在以下情况下你应该使用指针:
(1)考虑奥存在不指向任何对象的可能(在这种情况下,可以设置指针为空);
(2)需要在不同的时刻指向不同的对象(在这种情况下,可以改变指针的指向);

   总体来说,在以下情况你应该使用引用:
(1)如果总是指向一个对象并且一旦指向一个对象就不会再改变指向;
(2)当你重载某个操作符时,你应该使用引用,避免不必要的语义误解;

#include "StdAfx.h"#include <iostream> using namespace std;int temp; //定义全局变量tempvoid swap(int &p1, int &p2){ //此处函数的形参p1, p2都是引用(引用作为参数)int p;                         //它以返回值的方法返回函数值p=p1;p1=p2; p2=p; }int &fn2(int r) //定义函数fn2,它以引用方式返回函数值{temp=(int)(r*r*3.14);return temp;}int main( ){int a,b;cin>>a>>b;   swap(a,b);    //直接以变量a和b作为实参调用swap函数cout<<a<<" "<< b <<endl; int c=fn2(a); //系统生成返回值的副本,可以从被调函数中返回一个全局变量的引用int &d=fn2(a); //系统不生成返回值的副本,可以从被调函数中返回一个全局变量的引用cout<<c<<endl;cout<<d<<endl;return 0;}

引用作为返回值,必须遵守以下规则:

  (1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了”无所指”的引用,程序会进入未知状态。

  (2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

  (3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

  (4)引用与一些操作符的重载:
  流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回 一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一 个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这 就是C++语言中引入引用这个概念的原因吧。 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

C++友元函数和友元类

  如果在本类以外的其他地方定义了一个函数(这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数),在类体中用friend对其进行声明,此函数就称为本类的友元函数。友元函数可以访问这个类中的私有成员:

#include "StdAfx.h"#include <iostream>using namespace std;class Time{public:  Time(int,int,int);   friend void display(Time &);  //声明display函数为Time类的友元函数private:                          //以下数据是私有数据成员   int hour;   int minute;   int sec;};Time::Time(int h,int m,int s)  //成员构造函数给hour,minute,sec赋初值{   hour=h;   minute=m;   sec=s;}void display(Time& t)  //这是友元函数,形参t是Time类对象的引用{   cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl;}int main( ){   Time t1(10,13,56);   display(t1);   return 0;  //调用display函数,实参t1是Time类对象}

  不仅可以将一个函数声明为一个类的“朋友”,而且可以将一个类(例如B类)声明为另一个类(例如A类)的“朋友”。这时B类就是A类的友元类。
使用友元类时注意:
(1)友元关系不能被继承。
(2)友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3)友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

C++标准库容器的基本用法

  c++标准模版库中,容器总共分为三类:顺序容器、关联容器、容器适配器。
  
  1,标准库的有序容器包括set、map、multiset、multimap四类,这类容器内部的元素始终是有序的,容器内部默认使用‘<’操作符完成元素大小的比较,使用者也可以提供自己的元素大小比较函数。这类容器增加元素只提供了insert操作,插入的元素由容器按其大小自动放到合适的位置。
  有序的容器的实现可以理解为是相同的,就是一棵平衡二叉树。set和multiset中,树节点包含的就是每个元素,并按元素的大小比较结果排序,set中不允许有相同的元素,而multiset则可以存在相同的元素。map和multimap中,每个树节点就是map的每个元素的键和值,并按键的大小比较结果排序,map中不允许有相同键的元素,而multimap则可以存在相同键的元素。

  2,无序容器即内部元素是没有排序的,但也不是乱序的,元素的顺序为元素加入容器中的顺序。这类容器包括vector、list、deque三类。这类容器提供了push_back和push_front操作,即在尾部或者是头部插入元素,insert操作可以在容器指定的位置插入元素。

 

是否连续内存

支持头部插入或删除元素

支持中间插入或删除元素

支持随机访问

vector

是(效率低)

list

是(效率很高)

deque

块内连续

是(效率高)



 3,还有一种是容器适配器。适配器顾名思义,就是让一个对象的行为符合另一个对象的行为的机制。容器适配器,就是一个接口,它让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。c++ STL 中包含三种适配器:栈stack 、队列queue 和优先级priority_queue。
  STL 中提供的三种适配器可以由某一种顺序容器去实现。默认下stack 和queue 基于deque 容器实现,priority_queue 则基于vector 容器实现。当然在创建一个适配器时也可以指定具体的实现容器,创建适配器时在第二个参数上指定具体的顺序容器可以覆盖适配器的默认实现。
  栈stack 的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作有求,它们都提供了push_back 、pop_back 和back 操作;
队列queue 的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front 操作,因此其不能建立在vector 容器上;
  优先级队列priority_queue 适配器要求提供随机访问功能,因此不能建立在list 容器上。

#include "StdAfx.h"#include <iostream>#include <string>#include <list>#include <map>using namespace std;    int main( ){        list<string> l;                       l.push_back("Hello");        l.push_back("World");        for(list<string>::iterator it=l.begin();it!=l.end();it++){        cout<<*it<<endl;        }        map<string,string> m;        m.insert(pair<string,string>("one","cat"));        m.insert(pair<string,string>("two","horse"));        cout<<m.at("one")<<endl;        //使用[]重载运算符        m["three"]="dog";        cout<<m.at("three")<<endl;       return 0;     }


C++字符串常用操作

#include "StdAfx.h"#include <iostream>#include <string>#include <sstream>using namespace std;int main( ){string str;str+="Hello";str+="World";cout<<str<<endl;stringstream ss; ss<<200; ss<<" "; ss<<"Pigs"; cout<<ss.str()<<endl; return 0;     }


C++文件操作

#include "StdAfx.h"#include <iostream>#include <fstream>#include <sstream>using namespace std;int main( ){    ofstream of("a.txt");    of<<"Hello World !";  //向文件中写入字符串    of.close();    ifstream inf("a.txt");//读取字符串    stringbuf sb;    inf>>&sb;    cout<<sb.str()<<endl;    return 0; }

类型组合(结构,数组和指针)

#include "stdafx.h"#include <iostream>using namespace std;struct Year{                            //结构体    int year;    int month;};int main(){    Year  y[3]={{1990,1},{1991,12}};//创建结构数组,说明:y是一个数组,y[0]是一个结构,y[0].year是该结构的一个成员    y[0].year=2000;              //使用成员运算符访问其成员    (y+1)->year=2001;           //由于数组名是一个指针,等价于y[1].year    cout<<(y+1)->year<<endl;    cout<<y[1].month<<endl;    Year y01,y02;                   //创建该类型变量    y01.year=2004;                //使用成员运算符访问其成员    Year *p=&y02;                //通过指针使用间接成员运算符访问其成员    p->year=2005;    const Year *q[2]={&y01,&y02};         //创建指针数组    cout<<q[0]->year<<endl;    const Year **ppa=q;                   //创建指向以上数组的指针    cout<<(*ppa)->year<<endl;    auto ppb=q;                            //与 Year **ppa=q 等价,auto提供方便推断出ppb类型    cout<<(*(ppb+1))->year<<endl;    return 0;}


数组,vector和array比较

1, C++中内置数组,简单方便;数组大小固定,速度较快;
   通用格式是:数据类型 数组名[ 数组大小 ];,
2, vector 是STL中的容器类,包含多种通用算法;
   长度可变,使用灵活,但效率稍低;
   vector是使用 new 和 delete 来管理内存的,
3,array 数组模板 ,在C++11中才支持;
   通用格式:array<类型名, 元素个数> 数组名;
   注意,因为长度固定,这里的元素个数不能是变量!长度固定,提供了更好、更安全的接口,执行效率和内置数组相同,可以有效替代内置数组

#include "stdafx.h"#include <iostream>#include <vector>#include <array>using namespace std;int main(){     int d[]={1,2,3,4,5,6,7};     //数组    string name[2];    name[0]="Zhang";    name[1]="Li";    vector<double> v(2);         //vector    v[0]=0.1;    v[1]=0.2;    array<double,2> a1={1.1,1.2}; //array    array<double,2> a2;    a2=a1;              //可以将一个array对象赋给另一个array对象;而对于数组,必须逐元素复制数据    cout<<d[1]<<" at "<<&d[1]<<endl;    cout<<v[1]<<" at "<<&v[1]<<endl;    cout<<a2[1]<<" at "<<&a2[1]<<endl;       //从地址可知,array对象和数组存储在相同的内存区域(即栈)中,而vector对象存储在另一个区域(自由存储区或堆)中    return 0;}
0 0
原创粉丝点击