C++基础知识学习笔记

基本语法

C面向过程思想:程序=(数据结构)+(算法)
 数据结构与算法分离,以算法(函数)为主。

C++面向对象思想:程序=(数据结构+算法)
 数据结构(属性)与算法(操作)绑成一个类,定义一个个对象
对象=(数据结构+算法)  ,程序=(对象+对象+对象+……)

面向对象程序设计的程序员有两类:
1.面向对象应用程序设计
2.类库的设计

头文件:类的声明            ---类的外部接口
       (成员函数在类声明中实现时,一般很简短,默认为内联函数)
源文件:类的成员函数定义     ---类的内部实现


c++新增关键字:
namespace  using  class
private protected public
bool true false
this new delete
friend explicit mutable  virtual  operator inline  template
catch throw try
static_cast dynamic_cast const_cast reinterpret_cast

1.命名空间
namespace  myspace  //命名空间(相当于声明一个类)
{
 int num=100;//变量
 void show() //函数
 {
  cout<<"num= "<<num<<endl;
 }
 namespace myspace1
 {
  int num =150;
 }
};

2.iostream.h  C92标准
  iostream    C99标准 std;

3.输入输出
cin>>   标准输入
cout<<  标准输出
cerr<<  标准错误输出
clog<<  错误输出(有缓冲)


4.布尔类型
   bool flag; 只能存放false 0和true 1 两种状态。
   flag = true ;
   flag = false;
   flag = 3 ;此时flag只能为true(1);

5.const
const num=12;//只读变量,定义时必须初始化。


6.引用和指针

区别:
1.非空: 引用不能为空,定义的时候必须初始化(指针可以为空,不初始化)
2.合法性:使用的时候不需要再验证期合法性。(指针使用的时候需要验证其合法性)
3.不可修改:初始化对象之后不可修改,但标示的变量值可变。(指针灵活,可随意修改所指对象及对象值)
4.应用场合:引用一般用在对象固定的单个变量,不能是数组。
5.引用不用分配内存,节省内存空间,适用于较大数据参数传递。

int count = 11;
int num = 12;
int &ref = num; //1.同一个内存空间,不同的名字 2.定义的时候必须初始化 3.标示对象固定,值可变
ref = count;
const int &ref=num;//常引用:即可提高效率,又保护数据不更改。
注意:char &ref="hello"是不对的,hello是const类型的常字符串,不能转换为char类型引用。

void myswap(int *n1, int *n2)//另开辟一指针变量空间存地址,其指向对象可改变,灵活。

void myswap(int &n1, int &n2) //可修改同一块内存空间的值。简单方便。但其标示对象不可变。

将“引用”作为函数参数有哪些特点?
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。


返回引用注意:
(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用,虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。 主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)流操作符重载返回值申明为“引用”的作用:
流操作符<<和>>,这两个操作符常常希望被连续使用;赋值操作符=。这个操作符象流操作符一样,是可以连续使用:x=y=10;
(5)在另外的一些操作符中,不能返回引用:+-*/ 四则运算符。它们不能返回引用,主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值.


什么时候需要“引用”?
流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用

7.内联函数
编译的时候直接替换成函数体,不需要分配栈空间,节省了时间。
适合于简短代码,不能有while switch等复杂结构流程语句。

#define  A(x)   x   在预处理的时候替换,无参数类型的制约,c++中一般不用
inline  fun1(int a)

inline  string dbtest (int a ,int b)  //声明的时候加上 inline
void main()

 dbtest(3,4)     
}

string dbtest(int a,int b)   //函数体,适用代码短,无复杂结构控制语句,无递归函数
{
 return a+b;           
}

 

8.内存分配
C:(函数)
//malloc分配的特点:1.按字节分配,返回void类型地址 2.不进行初始化 3.需要#include<stdlib>
int *p;
p = (int *)malloc(100 * sizeof(int));
free(p);
                
C++:new(关键字)
在C++中,类的创建如果用malloc则绕过了构造函数,不是一个真正的类创建。只是分配内存而已。
用new申请的是一个类对象的内存空间,调用了构造函数,由于堆是手动分配,不用的时候需要用delete手动释放,delete调用析构函数释放内存空间。不然容易造成内存泄露。反复new和delete容易造成内存碎片。

如果对象的数据成员包含指向堆空间的指针,必须自定义深拷贝构造函数,需要用new为创建的对象分配堆空间
一般自定义的拷贝构造函数都需要一个自定义的析构函数来释放额外的资源。

//new分配的特点:1按对象类型分配,返回对象类型地址 2.默认方式初始化 3.不需要头文件
Student *p;
p = new Student[5];
delete [] p;  //单一标量时。用 delete p;

A *p=new A; //会调用无参构造
A *p=new A[10];//调用10次无参构造,在堆上分配对象数组,只能调用无参构造,不能在跟参数。
A *p=new A(10); //调用有参构造
A *p=new A(a1);//调用拷贝构造
A *p=new A(*p);//调用拷贝构造

delete [] p;// 调用了析构函数。

**********************************************************************************
                 ---类和对象---


面向过程:C  功能分解,
面向对象(Object Oriented):C++  责任分解
特征:1.抽象(abstract) 2.封装(encapsulation)
 3.继承(inheritance) 4.多态(polymorphism)

类:同类型对象抽象出共性形成类
对象:是类的一个实例,除了具有类的共性以外,还会有自己的特性
关系:类是对象的一种抽象描述,对象是类的实例化

c++中,类与结构体区别:
1.结构体是类的一种特例,class可以改为struct。
2.未指定访问权限时,类中成员默认为私有,而结构体默认为公有。
3.一般,数据--结构体;  数据+操作行为--类

this指针:
1.类的所有对象调用的成员函数都是同一段代码,不同对象在调用函数的时候,会传给函数的隐含形参this指针一个自己的地址,相当于this=&s,这样函数操作就知道是哪个对象的数据了,也就是成员函数可以直接访问数据成员而无需对象名的原因。
2.对象的数据在main函数的栈里,别的函数不能直接访问,所以要传入对象的地址。(相当于传址调用)
3.线程中创建线程函数中的函数参数一般不能是c++的成员函数,因为在类中定义的成员函数编译的时候会自动加上this指针。(可以将该函数设定为static静态成员函数或者友元函数)


重载:类中允许多个同名不同参函数,编译器根据函数名和参数确定不同的函数调用。
重写(覆盖):子类重新定义父类虚函数。迟滞联编,运行的时候动态调用子类的函数。

类成员函数的重载、覆盖和隐藏区别?
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆


类大小:有它数据成员(不含static数据)所占空间决定。(类的成员函数不占内存空间)

class  <类名>
{
      public:
                 <公有数据成员和成员函数>;
      protected:
                 <保护数据成员和成员函数>;
    private:
                 <私有数据成员和成员函数>;

};
<各个成员函数的实现>;


---数据成员---

私有成员:private  :自身类(+友元类)
 要访问:1.加友元friend  2.用接口函数得到其值。

保护成员:protected :自身类(+友元类)+派生类

共有成员:public:自身类(+友元类)+派生类+其他类和函数 ==任何代码


静态成员:
静态数据成员:面向对象中使用静态数据成员替代全局变量,可以增加安全性和更好的封装重用特性。
静态成员函数:
*static数据成员:public:
       static int count;
      int Student::count =88;  //定义和初始化静态成员。
                  cout<<"count=" <<Student::count<<endl;//类的静态成员,一般通过类来访问。
   1. 在类中定义,在类外初始化(加域限定符),放在类的内部实现.cpp文件中。
         (不能放在类声明里面,也不能在.cpp文件中main函数前面)。
   2.属于类,不属于某个对象,存储于全局数据区,程序运行前便已分配内存和初始化。
        3.public类型静态数据可以在类外共享,private/protected只可在类内部访问,不能用this限制.
   4.利用类名直接访问(也可以利用对象访问)

        使用场合:一般用在保存流动变化的对象个数,
                 作为一个标志指示一个特定动作是否发生,
                 指向一个链表的头结点或尾节点。

 static +成员函数
        1.静态成员函数中没有this参数传入。
   2.一般为public,使用类名直接调用。

        public:
  static A * createObj()  //用static函数里new调用构造函数。
  {
   A *obj=new A;     //可调用构造函数
   return obj;
  }
         A * p= A::createObj();     //调用static函数

常成员
const+数据成员:const int num; 
 1.只读变量。
 2.允许int a[num]
 3.必须在构造函数的初始值表中初始化。
 Student() : num(10) {}
const+成员函数:void fun() const
      不能修改类中的数据成员(除了mutable int a;)

      

this指针:this->name
1.调用成员函数的时候将对象的指针作为隐含的参数传到行为函数中,联通对象(栈)和函数。使得函数可直接使用对应的对象中的数据成员。
2.this不能用在静态成员。
    

---成员函数---

构造函数 :类的独特性使得它对数据成员有保护性,不能随意赋值来初始化,可以用一个成员函数来专门进行初始化。为了方便,则让他在定义一个对象的时候自动调用特殊的成员函数(即构造函数)进行分配内存和初始化。

类型:
无参构造
有参构造

初始化方式:
a.函数体内赋值(已分配空间)。
b. 通过初始值表在分配空间的同时赋值。A(int _a):a(_a){},一般用于常量和引用。
  注意:构造对象成员的时候,得看类中声明的顺序,不是看冒号后面成员初始化的顺序。
加explicit表示确切赋值,阻止隐式赋给对象的成员(A a1=200;)

定义对象:(类的实例化)
Student st1;   //调用无参构造
          //不能是Student st1();因为C++会认为是声明了一个名叫st1的普通函数,返回Student类型。
Student st2(2,"xiaoming",80); //调用有参构造


1.定义对象的时候自动调用,给对象初始化
2.构造函数名和类名相同,没有返回类型
3.可重载,根据参数类型和顺序自动调用相对应构造函数。
4.默认会生成一个空的无参构造函数,只负责分配内存不进行初始化。(全局为0,局部为随机)
  一旦自己定义一个构造函数,那么默认的无参构造函数将不再提供。(记得手动加上无参构造函数)
5.构造函数可以是private(可利用public中static函数调用private中的构造函数来构造新对象)

无参构造函数
Student() {} 

有参构造函数
Student(int nu, char *na, float sc) {}

拷贝构造函数:
1.使用已经存在的对象来生成新的对象
Student(Student &b){} 
Student st4(st1); //显式调用拷贝构造函数
Student st5=st2;  //隐式调用拷贝构造函数(不是赋值)

当里面有指针时:
a.浅拷贝:A(A & _a1)
      {
        this->p=_a1.p;
      }
拷贝后指针指向同一个内存,使用时会发生错乱。
b.深拷贝:A(A & _a1)
      {
         p=new char[10];
   strcpy(p,_a1);
      }

手动实现拷贝构造函数。连指针指向的对象一起拷贝

2.当函数的形参是类的对象,实参传递给形参调用函数的时候。
//String & _s =s1 没有调用拷贝构造,仅是取了另一个名字。s代表s1本身;  节约内存。
//String  s=s1;  调用了拷贝构造函数,另分配了临时空间s;
Book test1(Book b)
{
    return b;
}
test1(book2);
3.函数的返回类型为对象类型的时候。
String    返回的是一个值, 不能返回局部变量地址,返回一个临时对象,返回后立即释放。
String &  返回的是一块内存,不能是局部变量内存,可以是全局变量。
String & operator +(String& _s)


析构函数
1.释放对象之前自动调用,手动释放堆,按栈的顺序(先构造后释放)释放内存空间。
2.函数名为~类名,没有返回类型
3.,没有参数,不可以重载
4.默认会生成一个空的析构函数。
5.析构函数必须是public

其他自定义接口函数:
 float get_score() //读取数据成员,对于类中只包含有get而不包含有set的属性叫只读属性
 {
     return score;
 }
 void set_score(float sc)  //修改数据成员,包含有set方法的数据成员叫做可写的属性
 {
         score = sc;
 }

带有默认值参数的函数
省去了多个函数因参数不同而进行重载,默认值的参数一般是从右到左
int add(int a=2,int b=1) {}
cout<<add(10,20)<<endl;
cout<<add(10)<<endl;


友元:
 友元超越了类的封装,在类外允许访问类的私有数据成员,带来性能提升和编程的灵活性。  
 1.友元函数(普通函数 + 类的成员函数)
 函数声明在类声明中,实现必须放在要访问的类的实现之后。(注意:不属于类的成员函数)
 friend float avg_score(Student *ps, int count);
 friend void Teacher::set_stu_score(Student &ps, float sc);

 2.友元类:可以用该类所有成员函数访问私有数据成员
 该友元类的定义应放在有私有数据声明前面。
 friend class Teacher;

     
          ­
*******************************************************************************
                ---类的继承和派生---

父类(基类)
 ↓
子类(派生类)
1.实现继承:全部继承,无需额外编码
2.接口继承:仅使用属性和方法,在子类中实现(抽象基类)
2.可视继承:子窗体使用父窗体的外观和实现代码


子类声明:
此时,子类拥有了父类的属性和方法,还可以自己添加独有的属性和方法,也可以重新实现或重载。

单继承格式:
class  <派生类名> :<继承方式> <基类名>
{
 public: //派生类新定义成员
 protected:
 private:
}

1.继承的方式--数据成员的访问权限:

private  :公有,保护-> 私有 (基类中私有数据不能在派生类中访问,更不能在其他类和函数中访问)

protected:公有 -> 保护     (基类中保护数据可以在派生类中访问,不能在其他类和函数中访问  )

public:   所有不变。        (基类中公有数据可以在派生类中访问,也可以在其他类和函数中访问)

1.私有数据在任何方式下继承都是不能访问的。
2.保护数据在私有继承的情况下只能在派生类中访问,继续继承则不能访问了。
3.公有数据在公有继承情况下都能访问。

2.构造函数的实现方式:
注意: 1.派生类不能初始化基类中的私有数据。
 2.默认为先调用基类的无参构造函数,再调用派生类的构造函数。
所以:一般可以指定调用基类构造函数的方式(注意按照基类构造函数的参数顺序进行传递)。
 使得用合适的基类构造基类的数据,派生类构造派生类的数据。
Car::Car(float ai, float sp, int to) : Vehicle(to, sp)

3.成员函数的实现方式:
a.仅继承基类的成员函数
  则直接调用基类的版本。

b.对基类成员函数进行了重新的实现(同名同参)
基类函数没有virtual关键字,原来的基类成员函数被隐藏
只调用派生类的成员函数   lex.show();
可以通过基类名::成员函数 的方式调用基类的版本。
lex.Vehicle::show();

c.对基类成员函数进行了重新的实现(同名不同参)
不管基类函数有没有virtual关键字,原来的基类成员函数被隐藏
只能调用派生类的成员函数  lex.stop(3);
可以通过基类名::成员函数 的方式调用基类的版本。
lex.Vehicle::stop();

 

多态
同一个基类有多个派生类,每个派生类有相同方法名,不同方法体,允许将子类的地址赋值给父类指针或引用。
多态实现条件:
1.必须是公有继承
2.必须是基类指针或引用对象
3.必须是虚函数

多态的作用:
1)隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
2)接口重用,为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用

基类指针或引用可以指向派生类对象
base1 *c1=new child;
c1->fun();

虚函数
virtual修饰的基类方法
同样一个操作,在继承中的不同对象常常需要不同的实现。比如学生和研究生的学费计算。称为多态。
基类和子类都是属于基类的,用基类的指针或引用调用基类和子类都有的同名同参的函数时,需要根据不同的对象类型调用不同的函数,用virtual表示该函数为迟后联编(late binding),运行的时候才知道调用哪个对象的函数。这样便可实现用一个函数操作便可以管理所有的继承的对象。
1.只有成员函数才能是虚函数,静态成员函数、内联函数、构造函数不能是虚函数。
2.析构函数需要针对不同对象析构不同对象,可以是虚函数。

1.虚函数在基类中必须实现函数。
2.虚函数可以被派生类重写,没有重写时调用基类函数。

纯虚函数
程序在功能分解过程中,高层次的类只用来继承,没有必要实例化,称为抽象类,抽象类为其他类继承服务。
其子类对其纯虚函数进行了实现,则不是抽象类了。
里面的函数只有声明没有实现,称为纯虚函数。纯虚函数为子类保留一个位置,使得在多态中可以使用子类的实在函数覆盖原来的位置。不然基类的指针找不到该成员函数会报错。
方法体为0的虚函数,在每个派生类(要实例化)中必须实现函数。
virtual char* what()=0;

抽象类
只要包含有一个纯虚函数的类为抽象类
1.抽象类一般为基类
2.抽象类可以包含其他的实虚函数
3.抽象类不能被实例化,但可以声明一个抽象类的指针或引用。
4.派生类如果没有重写基类的纯虚函数,该派生类也为抽象类

 

动态联编与静态联编
动态联编和静态联编只能在多态时使用
动态联编:在调用时,确定基类指针指向对象
静态联编:在编译时,确定基类指针所指对象


虚函数表

类大小计算

1.没有数据成员大小为1
2.没有虚函数的类,只给非静态成员变量分配空间
3.派生类继承基类的所有成员大小
4.有虚函数的基类会多分配4个字节大小存放虚表地址
5.派生类继承所有基类的存放虚表地址空间
6.派生类自己的方法不再分配空间,替换基类的方法或默认添加到第一个基类虚表后面

虚表(v-table)
保存类中虚函数的地址
包含虚函数类的对象:前四个字节肯定是虚表地址


多继承
一个子类继承多个父类:
1.编译程序问题:构造的顺序
2.模糊性问题:多个基类具有相同方法名,到底调用哪一个。

虚基类
主要是用来解决多个基类具有相同的方法名的问题,派生类继承多个基类时,发现存在虚基类则构造,后加入的虚基类用前面的那个。
class  base
class base1:virtual public base
class base2:virtual public base
class child:public base1,public base2
包含虚基类构造顺序:
1.首先构造虚基类
2.构造非虚基类


**********************************************************************************
        ---模板---

函数模板
函数模板避免了针对相同操作、不同数据类型需要多次重载不同的函数的局限。
template<class T,class T1,.....>
T fun(T a,T1 b)
{
    ...
}
函数模板:声明了一个通用类型参数的函数,编译时不为其产生任何代码。
模板函数:编译系统发现具体的函数调用的时候自动匹配,找到函数模板就重载一个实际的模板函数。

类模板
在定义类时,类中的数据类型可以不确定,但是在实例化对象时指定数据类型

C++中为什么用模板类:
 (1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型

template<class T,class T1>
class A
{
 public:
  A(T _a,T1 _b);
     T1 max();
  T min();
 private:
  T a;
     T1 b;
};
  template<class T,class T1>
  T1 A<T,T1>::max()
  {
     return b;
  }

A<int,char> a1(2,‘a‘); //实例化类对象时必须要指定数据类型     

函数模板与类模板有什么区别:
函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

友元模板类

templat<class T>
clas B;//前置声明B为模板类

template <class T>
class A
{
    friend class B<T>;  //说明模板类B为友元类
};
template <class T>
class B
{
}

模板类继承


异常处理

异常
正在运行程序发生错误
异常是可能也可能不发生,根据程序运行的环境或输入值有关
一旦发生异常,程序停止运行,不会退出程序

异常处理:
1.抛出异常  throw
2.捕捉异常  try
3.处理异常  catch

try
{
    throw except("b==0 is error"); //抛出异常
}

catch(base& e)
{
    cout<<e.what()<<endl;
}


c++如何调用c的函数

c++是一种不彻底的面向对象语言,为了和c兼容,它可以定义不属于任何类的全局变量和函数。
但是为了支持函数的重载,C++对函数的处理方式为:
int add(int a,int b)
c++编译之后函数为: add_int_int
c编译之后函数为:add

一般文件结构:
#ifndef __INCxvWorksh
#define __INCxvWorksh

#ifdef __cplusplus
extern "C"
{
#endif

/*C code*/

#ifdef __cplusplus
}
#endif

extern "C":1.按照c编译语法编译
           2.extern表示它修饰的全局变量和函数可以在本模块和其他模块中使用


判断一段程序是由C 编译程序还是由C++编译程序编译的:
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endi

//指向对象的数据成员指针
int *p=&(a1.x);
cout<<*p<<endl;

//指向类中的成员函数指针
int (A::* fun) ();  
fun=&A::getX;
cout<<(a1.*fun)()<<endl;


符号重载(重写)
符号重载:给符号赋予新的功能
相当于一个特殊的成员函数或者友元函数,只是函数名为符号。
s1+s2相当于 s1.+(s2)  :s1调用 名字为‘+’的成员函数

符号
运算符号:+ - * / %
比较符号:> >= <  <= != ==
逻辑符号:&& ||
位运算符号:& | ^ ~
赋值符号:= += -=
特殊符号:[] . , :: -> * &


可用作重载的运算符:
 算术运算符:+,-,*,/,%,++,--
  位操作运算符:&,|,~,^,<<,>>
  逻辑运算符:!,&&,||;
  比较运算符:<,>,>=,<=,==,!=;
  赋值运算符:=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=;
  其他运算符:[],(),->,,(逗号运算符)

不能重写的符号:
 .
 ::
 *
 ?:
 sizeof

符号重写语法:
返回值类型  operator  重写符号(参数)

符号重载方式:
1.类的成员方式重载
2.友元函数方式重载
 
符号重写特点:
1.不能改变符号的结合性
2.不能改变符号的优先级
3.不能改变符号元数(一元符号 二元符号)
4.不能创造符号

类的成员方式重写与友元方式重写特点:
a.类的成员方式重写特点
  1.一元符号,可以写成无参数(this指针代替)
  2.二元符号,可以写成只有一个参数(this指针代替另一参数)
  3. =,(),[] 和-> 只能是成员方式重写
b.友元方式重写特点
  1.一元符号,必须有一个参数
  2.二元符号,必须两个参数
  3.<<, >>只能使用元方式重写

 

C++流

所有流的基类:ios(抽象类) -->istream ,ostream ,fstreambase,strseambase
  istream +fstreambase --> ifstream
  ostream +fstreambase --> ofstream

  istream +strseambase --> istrstream
  ostream +strseambase --> ostrstream 

  istream +ostream  --> iostream
  iostream+fstreambase  --> fstream
  iostream+strseambase  --> strstream

头文件:iostream: ios,istream,ostream,iostream,
   fstream : fstreambase,ifstream,ofstream,fstream, +<iostream>
       strstream:strstreambase,istrstream,ostrstream,strstream +<iostream>

标准输入输出流:istream ostream iostream

文件流:ifstream(读文件) ofstream(写文件) fstream
文件(键盘)  ==》 内存   读入   in
文件(屏幕) 《==  内存   写出   out
字符串流: istrstream  ostrstream  strstream

 

输入/输出流的控制
输入流:cin
输出流:cout cerr  clog(日志)
cout:标准输出,与缓存关联
cerr:错误输出,无缓存
clog:日志输出,与缓存关联

‘\n‘ endl  回车 清空缓存

#include<iomanip> 输入输流控制的头文件
输入输出流的控制符
#include<iomanip>   I/O流控制头文件
dec                                              设置整数的基数为10
hex                                              设置整数的基数为16
oct                                              设置整数的基数为8
setfill(c)                                       设置填充字符c,c可以是字符常量或字符变量
setprecision(n)                                  设置有效数字个数
setw(n)                                        设置字段宽度为n位

setiosflags(ios::scientific)                     设置浮点数以科学计数法(即指数)显示
setiosflags(ios::left)                           输出数据左对齐   
setiosflags(ios::right)                          输出数据右对齐   
setiosflags(ios::uppercase)                      在以科学计数法输出E和以十六进制输出字母X时以大写表示
setiosflags(ios::showpos)                       输出正数时给出“+”号

控制输出格式的流成员函数
precision(n)    设置实数的精度为n位
width(n)        设置字段宽度为n位
fill(c)         设置填充字符c
setf()          设置输出格式状态  
unsetf()        终止已设置的输出格式状态     


格式状态标志
 
ios_base::left
左对齐输出,用于输出
 
ios_base::right
右对齐输出,用于输出
 
 
ios_base::dec
转换基数为十进制,用于输入或输出
 
ios_base::oct
转换基数为八进制,用于输入或输出
 
ios_base::hex
转换基数为十六进制,用于输入或输出
 
ios_base::showbase
输出时显示基指示符(0表示八进制,0x或0X表示十六进制),用于输入或输出
 
 
ios_base::uppercase
输出时表示十六进制的x为大写,表示浮点数科学计数法的e为大写,用于输出
 
ios_base::showpos
正整数前显示“+”符号,用于输出
 
ios_base::scientific
用科学表示法显示浮点数,用于输出
 

 

 


文件流:
ifstream:读文件
ofstream:写文件

文件流操作文件模式:
ios::in:读文件
ios::out:写文件(创建文件,清空间文件,写入)
ios::app:写文件追加
ios::ate:写文件追加
ios::binary:二进制文件
ios::trunc:清空文件内容

out,trunc和app模式只能用于指定与ofstream或fstream对象关联的文件

in模式只能用于指定与ifstream或fstream对象关联的文件

流构造:
ifstream(char* filename,mode)

关闭流
close(ifstream)//清空缓存(缓存中的内容写入到文件)


get
语法:
  istream &get( char &ch );
  istream &get( char *buffer, streamsize num );
  istream &get( char *buffer, streamsize num, char delim );

get()函数被用于输入流:
读入一个字符并把它存储在ch,
读取字符到buffer直到num - 1个字符被读入, 或者碰到EOF或换行标志,
读取字符到buffer直到已读入num - 1 个字符,或者碰到EOF或delim(delim直到下一次不会被读取)


getline
语法:
  istream &getline( char *buffer, streamsize num );
  istream &getline( char *buffer, streamsize num, char delim );

getline()函数用于输入流,读取字符到buffer中直到下列情况发生:
num - 1个字符已经读入,
碰到一个换行标志,
碰到一个EOF,
或者,任意地读入,直到读到字符delim。delim字符不会被放入buffer中。


块读写
read(char * buf,int size);//一般与输入流使用
write(char * buf,int size);//一般是与输出流使用

size:写入或读取的字节数(n*sizeof(class))


标准模板库

C++:1:面向对象设计思想,抽象封装,继承多态,标准类库
     2:泛型程序设计思想(generic programming),模板机制,STL

STL :Standard Template Library(数据结构+算法)
容器 :可容纳各种数据类型的数据结构。
算法:用来操作容器中的元素的算法函数。

容器
分类:
第一类容器:
1.顺序容器:vector,list,deque
2.关联容器:set(multiset)  map(multimap) (key)

第二类容器:
3.容器适配器:stack,queue,priority_queue

vector
1.vector是一种顺序容器,元素是存放在一段连续空间。
2.相当于能够存放任意类型的动态数组,代替C类型的。
3.在数组末尾增删元素时间固定,中间增删时间与该元素后面个数成正比。
4.支持随机存储。
5.可以用下标[],at(),迭代器访问,at在运行时会检查容器大小,更安全。

构造vector
 vector();                                          //无参构造
 vector(size_type num, const TYPE &val );           //有参,放入num个val
 vector( const vector &from );                      //拷贝构造
 vector( input_iterator start, input_iterator end );//[start,end)区间元素
读取:
 begin(); //返回第一个元素的迭代器
 end(); //指向当前vector末尾元素的下一位置的迭代器

 rend();  // 返回Vector起始的逆迭代器
 rbegin();// 返回Vector尾部的逆迭代器
 
 TYPE at( size_type loc );//返回当前Vector指定位置loc的元素的引用.
     //v.at(i)
 front(); //返回第一个元素
 back();  //返回最后一个元素

增:
 void push_back( const TYPE &val ); //存放元素到容器末尾
  //push_back(20);
 iterator insert( iterator loc, const TYPE &val );
  //vector<int>::iterator it1=v1.insert(it,2000);//插入新元素到it指定元素前面
  //cout<<*it1<<endl;//返回值为迭代器执行插入元素位置 
      void insert( iterator loc, size_type num, const TYPE &val );
  //v1.insert(it,5,1);//在it所指元素前插入5个1;
      void insert( iterator loc, input_iterator start, input_iterator end );
  //v1.insert(it,v2.begin(),v2.end());//在指定位置it前插入区间[begin, end)的所有元素

删:
 pop_back();  //移除容器的末尾元素
 clear();     //清空所有元素

 iterator erase( iterator loc );  //删除指定元素
  v1.erase(v1.begin()+3);//通过迭代器删除容器中的元素
      iterator erase( iterator start, iterator end );
  v1.erase(v1.begin(),v1.begin()+4);//删除指定区间元素
改:
 void assign( input_iterator start, input_iterator end ); //将区间[start, end)的元素赋到           当前vector
   void assign( size_type num, const TYPE &val );//赋num个值为val的元素到vector中

 void swap( vector &from );//swap()函数交换当前vector与vector from的元素
  v1.swap(v5);//交换容器

查:
 #include <algorithm>   find为宏函数
  vector<int>::iterator it=find(v1.begin(),v1.end(),20);//查找find(算法函数)
  判断元素是否存在
     if(it==v1.end())
   cout<<"not find"<<endl;
  else
             v1.erase(it);

运算:v1 == v2   1.它们具有相同的容量  2. 所有相同位置的元素相等
   v1 != v2
   v1 <= v2
   v1 >= v2
   v1 < v2
   v1 > v2
   v[]

其他:
 size();    // 返回Vector元素数量的大小
  void resize( size_type size, TYPE val ); //改变Vector元素数量的大小
 max_size();// 返回Vector所能容纳元素的最大数量(上限)
 capacity();//返回vector所能容纳的元素数量(在不重新分配内存的情况下)
 empty();   // 判断Vector是否为空(返回true时为空)

 

 


迭代器
是一个自定类型,指向容器中元素的指针,每一种容器都有对应迭代器
 
迭代器分类:
1.一般迭代器
一般迭代器定义
vector<int>::iterator it;
2.只读迭代器(只能访问容器元素,不能修改)
只读迭代器定义
vector<int>::const_iterator it;
3.反转迭代器
反转迭代器定义
vector<int>::reverse_iterator it;

list 链表容器
1.元素按顺序储存在链表中.与向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢.
2.可以存放任何数据类型的动态链表
3.在任何位置插入删除都是常数时间
4.不支持随机存储。
5.

    
构造
 list()
 list(list& l)
 list(a,a+3);

在顺序容器中新增加的函数:

push_front: 在前面插入
pop_front: 删除前面的元素
merge: 合并两个链表,并清空被合并的那个
sort: 排序( list 不支持STL 的算法sort)
reverse: 颠倒链表
remove: 删除和指定值相等的所有元素
unique: 删除所有和前一个元素相同的元素
splice: 在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素


pair 
pair<int,string>
pair<list<int>::iterator,list<int>::iterator>


set关联容器
有序存放元素值
set容器构造
set<int> s1;
set<int,cmp>
set容器中元素值一定是唯一

multiset关联容器与set容器功能一样,但允许出现相同值的元素

map
一种key-value元素对容器
map<key,value>
key特点:
1.只读
2.唯一
3.根据key排序

map构造
map<string,int>

multimap关联容器与map容器功能一样,允许出现相同键值

练习:
1.实现vector list容器

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

C++基础知识学习笔记,布布扣,bubuko.com

C++基础知识学习笔记

上一篇:javascript实现数据结构:稀疏矩阵的十字链表存储表示


下一篇:[leetcode]Triangle @ Python