C++ 收尾篇

简介

此篇章为C++收尾篇。考前放出

C++

OOP基础功能收尾

  1. 对象赋值 ObjectName_2 = ObjectName_1; 数据成员直B接拷贝,成员函数为拷贝,这是因为在声明类的时候成员函数就初始化在堆中了。
  2. Box(int=10,int=10,int=10); int Box(int h, int w, int len){} 在初始声明时就可以给与默认值
  3. 对象拷贝
    ClassName ObjectName(ObjectName1);
    将类的对象类似值传递没有加&给函数时,也是一种对象的拷贝
    把对象作为返回值,也是一种拷贝
  4. Copy Constructor
    ClassName ObjectName2(ClassName & ObjectName1)
    拷贝构造函数的第一个参数必须是一个对象的引用,用于调用原型对象,所以有“拷贝”之称。
    int Box::Box(const Box &temObj){ height = temObj.height + 1}

运算符重载

  1. 用成员函数实现复数加法:Complex c3 = c1.add(c2); 这是使用拷贝函数的方法。
    假若使用运算符重载,则如
class Complex
{
	public:
		Complex operator+(Complex &c2);
	private:
		double real;
		double imag;
}
Complex Complex::add(Complex &c2)
{
	return Complex(real+c2.real, imag+c2.imag) //运算符重载在c1中,real和imag是c1的
}
  1. operator加运算符代表运算符重载,此时 c1.operator(c2) 和 c1+c2 是等价的
  2. 运输符重载的限制:五个运算符不能重载,重载不能改变运算符的一些性质,不能有运算符(即必须有参数),参数至少有一个是类的对象或其引用。
  3. 使用运输符重载用成员函数或友元函数,以便访问里面private的数据,如c1的实部和虚部 运算符重载友元函数定义方式: Complex operator + (Complex &c1, double &d){…}
  4. 当前对象在重载中时,默认有 operator + (Complex *this, Complex &c2) 此时不能 Complex c3 = 3.14 + c2;
  5. 运算符重载友元函数定义方式: Complex operator + (Complex &c1, double &d){…} 声明方式 friend Complex operator + ()
    注意由于不是成员函数,所以参数没有默认 *this,要自己加。然而这更加灵活,可以 Complex operator + (double &d, Complex &c1){…} 这样就可以实现 Complex c3 = 3.14 + c2;
    所以双目运算符用友元函数做重载比较好。重载>,<,==的时候不妨使用friend bool operator …
  6. 重载单目运算符
    ++前置和后置 使用operator( ); 是前置 operator(int) 是后置
Time Time::operator++(){
	sec++;
	return *this;
}
Time Time::operator++(int){
	Time temp(*this);
	sec++
	return temp;
}
  1. ”>>“和“<<”只能用友元函数作为运算符重载,函数的类型(返回值)和第一个参数都是 &ostream 和 &istream
public:
	friend ostream& oprator >> (ostream&,Complex&);

ostream& operator >> (ostream& output, Complex& c){
	cout << c.real << "+" << c.imag << "i";
} 
  1. 一点细节:用引用作为形参,减少时间空间开销;返回值是引用时,返回的不是常量,是引用所代表的对象。类里面的指针成员不是太好直接拷贝,所以尽量自己设计拷贝构造函数,给它开空间。

转换构造函数和类型转换函数

  1. 转换函数可以将一个指定类型的数据转换为类的对象,但是不能把一个类的对象转换为数据。
  2. operator 类型名( ) 没有参数 返回值的类型由类型名指定

继承与派生

  1. 单继承,派生类只从一个基类派生。多继承,派生类从多个类中派生。

  2. 派生类的声明方式:class Student1: public Student{...};
    继承有public,private,protected一样。默认私有继承。

  3. 简单派生类的构造函数,冒号代表要调用原构造函数传递参数。假若不传递参数给基类构造函数,则不需要冒号加东西。系统会自动调用基类的默认构造函数。基类构造函数若有没有默认值的参数

    public:
    	Student1(int n,string nam, char s, int a, string ad): Student(n,nam,s)
    	{age = a; addr=ad;}
    	派生类构造函数名(总形式参数表列) :基类构造函数名(实际参数表类)
    
  4. 一般来说可以只在类体中写声明,声明不必加冒号。外部再Student1::Student1(...)
    也可以使用初始化表

    Student1(int n,string nam, char s, int a, string ad): 
    Student(n,nam,s), age(a), addr(ad){}
    
  5. 基类构造函数->派生类构造函数->派生类析构函数->基类析构函数

  6. 有子对象的派生类的构造函数

  7.   Student1(int n,string nam, int n1, string nam1): 
      Student(n,nam), monitor(n1,nam1){} //调用子对象构造函数了个monitor
    
  8. 多层派生的时候的构造函数只用调用上一层构造函数
    Student2(...): Student1(...){...}

  9. 派生类成员的访问权限
    (1)派生类和基类自己访问自己的成员 一切均可
    (2)派生类外访问基类和派生类的成员 private, protected不可,public可
    (3)基类访问派生类成员 不可能
    (4)派生类访问基类成员

    1. public公有继承 所有成员仍为原有权限
    2. private私有继承 所有成员全部为私有权限
    3. protected保护继承 私有仍为私有 公有和保护为保护
  10. 保护成员不能被外界引用,但是可以被派生类使用。即可以在派生类中添加方法调用基类中的成员,而不必使用基类中的方法。
    我想要一些成员只应用在一套类的体系中,而不被外部访问时,就有protected。不需要再往下继承的类的功能可用private把它隐蔽起来。
    继承上,公有继承保护性不变,保护继承稍微保护了原来的public,私有继承全部保护了。常用公有继承。

  11. 多重继承
    优点:自然做到对单继承的扩展,可以继承多个类的功能
    缺点:结果复杂化,优先顺序模糊,功能冲突。
    声明方法:

    class D: public A, private B, protected C {...}
    
  12. 二义性:很多时候多个基类都有名字相同的成员,调用时要加命名域。
    c1.A::a=3; c1.A::display(); c1为A类派生类C的实例
    同名覆盖:基类的同名成员在派生类中被屏蔽,成为"不可见"的。
    若是方法,参数列表一样则覆盖,参数列表不一样则重载。

  13. 虚基类:A和B同继承自基类N,C继承自A和B。C++提供虚基类,在间接继承同一份基类时只保留一份成员
    声明方法:

    class A{...};
    class B : virtual public A 
    {...};
    class C : virtual public A 
    {...};
    class D:  public B, public C
    {...};
    //虚基类的初始化还要特别针对原基类进行初始化
    
  14. 基类与派生类的转换
    不同类型数据之间的自动转换和赋值,称为赋值兼容。
    只有共用派生类才是基类真正的子类型,它完整地继承了基类的功能
    派生类对象可以向基类对象赋值。1.赋值时舍弃派生类自己的成员2.赋值只是对数据成员赋值,对成员函数不存在赋值问题3.仍不能通过基类访问派生类新增的成员。

  15. 可以通过派生类对象对基类进行赋值或初始化

    class A{...};
    class B: public A {...} ;
    A a1; B b1
    A& r1=a1; //r1是引用别名
    A& r2=b1; //指向与a1起始位置相同的地址的指针 
    //基类的指针可以指向派生类,反过来不可以
    

    这意味着派生类对象可以被当作基类来引用,做为别的函数的参数

  16. 类的组合:类中的成员数据是另一个类的对象。如Line里有两个Dot的对象。

多态性

  1. 多态性:一个接口,多种方法

  2. 重载参数不一样,是静态多态,是编译时的多态性。而函数覆盖(虚函数)是针对每个类不一样,称为动态多态,是运行时的多态性。

  3. 虚函数函数覆盖:

    1. virtual声明成员函数为虚函数,可以在派生类中重新定义此函数。在类外定义虚函数时,不需要再加virtual。
    2. 派生类中重新定义此函数。要求函数名、函数类型、函数参数个数和类型全部与基函数相同。当一个成员函数被声明为虚函数后,派生类中同名函数(重载函数)也自动成为虚函数。但最好再加个virtual。若派生类中没有对基类虚函数重新定义,则直接继承基类虚函数。
    3. 定义一个指向基类对象的指针变量或引用变量,并使它指向同一类族中需要调用该函数的对象
    4. 通过指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
    Cylinder Cy1;
    Cylinder *p = Cy1;
    Cycle *q = Cy1;
    在虚函数的情况下 q->area()调用Cylinder的area函数。
    非虚函数的情况下 q->area()调用基类(Cycle)的area函数。
    p->area()都是调用Cylinder的area函数
    
  4. 使用方法
    先定义虚函数,然后定义指向基类的指针,用这个指针就可以不停地指向同一类族中不同对象的同名函数

  5. 假若定义的是非虚函数,则用基类指针调用成员函数,系统调用基类成员函数。假如使用派生类指针调用该成员函数,则系统会调用派生类对象中的成员函数。 简而言之,虚函数同意了派生类函数对基类函数的抹消。

  6. 理解关联(指针)、静态关联(重载、通过对象名调用的虚函数)、动态关联(基类指针调用虚函数)

  7. 排他性:
    一个成员函数被声明为虚函数后,同一类族中的类就不能再定义非virtual的非重载同名函数了。默认其为virtual,会直接函数覆盖。

  8. 何时使用虚函数:
    1.功能是否可能被更改且与参数无关,比如打印area,把函数声明为虚函数。
    2.成员函数如果是通过基类的指针或引用去访问,则应当声明为虚函数
    3. 有时候基类并不定义其函数体,功能等待派生类去添加,则virtual一个虚函数名。这称为纯虚函数。表现了基类定义接口,基类固定了函数名称、参数、返回类型。

  9. 虚析构函数:用指针销毁派生类的时候,假如是非虚析构函数,则会依照指针类型销毁,造成内存泄漏。所以通过声明虚析构函数,用任意类型指针销毁派生类都不会出现问题。

  10. 纯虚函数

    virtual float area() const = 0; // 纯虚函数    也可以不const直接 = 0;
    virtual float area() const {return 0}; // 虚函数,有{...}
    virtual 函数类型 函数名 (参数表类) = 0;
    
  11. 抽象类
    有一些类不是用来生成对象的,只是单纯用来定义一些基本类型用作继承。通常称为抽象基类。如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。

输入输出流

基本概念
  1. 对系统指定的标准设备的输入输出—— 标准I/O
    对外存磁盘为对象的输入输出——文件I/O
    对内存中指定的空间进行的输入输出——串I/O
  2. 流对象cin、cout和流运算符的定义存放在C++的输入输出流库中
  3. 内存中为每一个数据流开辟一个内存缓冲区,缓冲区中的数据就是流
  4. C++中输入输出流被定义为类,C++中I/O库中的类称为流类
  5. 类库有关的一些头文件:
    1. fstream 用于文件I/O
    2. strstream 用于字符串流I/O
    3. iomanip 用于格式化I/O
  6. iostream定义的流对象
    1. cin——从标准输入设备(键盘)输入到内存的数据流对象
    2. cout——从内存数据输出到标准输出设备(屏幕)
    3. cerr——输出错误
    4. clog——输出调试
  7. iostream定义的重载运算符
    <<和>>针对后面的变量类型进行了重载。
  8. 输入输出重定向及其应用(相当于重定义cin,cout的流对象)
    FILE *a = NULL;
    FILE *b = NULL;								//初始化文件指针(文件句柄)
    a = fopen("input.txt","r");                    //打开文件,打开方式为输入,用文件句柄a表示
    b = fopen("output.txt","w");				  //打开文件,打开方式为输出,用文件句柄b表示
    ch = get(a);
    put(b);
    freopen("input.txt",'r',stdin);				//输入重定向
    freopen("output.txt",'w',stdout); 			//输出重定向
    fclose(stdout);							//关闭重定向输出文件,标准输出还原为屏幕
    fclose(stdin);							//关闭重定向输入文件,标准输入还原为键盘
    fclose(a);
    fclose(b);
    
    注意,fopen等是C语言的函数,返回值为指向文件的指针。使用“文件句柄=fopen”去接收指针。
    而下文标准输入输出流是C++的函数,ifstream和ofstream是类,使用“类名 流对象名” 创建流对象。
标准输出流
  1. cout (console output 控制台输出) 向标准输出设备输出
    可以重定向输出到磁盘文件
  2. cerr 标准错误流,像标准错误设备输出有关错误信息。
    cerr流中的信息只能在显示器输出
  3. clog 流对象也是标准错误流。
    clog将信息存放在在缓冲区中,缓冲区满后或遇到endl时向显示器输出。
  4. 控制符控制输出格式
  5. ostream的一些成员函数
    cout.put('a'); //输出单个字符
    cout.put(71).put(79).put(68); //连续输出单个字符
    
标准输入流
  1. cin (console input 控制台输入)
    程序遍历通过流提取符">>"从cin流中提取对象
    ">>"从流中提取数据常常连续获得空格、\n、\t。直到打下回车才结束
  2. istrean的一些成员函数
文件的读写
  1. ASCII文件(纯文本文件) .txt, .cpp, .doc 与cout看起来一样的文件
  2. 二进制文件:.exe, 与内存中看起来一样的文件
  3. 文件流是以外存文件为输入输出对象的数据流
    每一个文件流都有一个内存缓冲区与之对应
    cout、cin 已经定义好了流对象
    infile、outfile 需要自定义对象
  4. 打开磁盘文件,就是将流对象和外部存储的文件关联起来
    ofstream outfile; //定义输出文件流类对象
    outfile.open("f1.dat", ios::out); 
    \\文件流对象.open(磁盘文件名,输入输出方式);
    
    ofstream outfile("c:\new\f1.dat",ios::out);
    \\文件流对象(磁盘文件名,输入输出方式);
    
    \\输入输出方式 ios::in, ios::out, ios::app, ios::ate
    \\打开的文件存在与否ios::trunc, ios::nocreate, ios::noreplace
    \\使用位或运算符|组合输入输出方式 ios::in | ios::out;
    
  5. 关闭文件
    outfile.close();
    //终止对outfile这个流对象的操作。
    
  6. 对ASCII文件的操作
    1. 以文件句柄为对象名,可以用已经重载运算符的"<<"">>"输入输出标准类型的数据
    2. 以文件句柄为对象名,可以用iostream的put,get,getline等成员函数进行字符的输入输出
    \\输出文件
    oftstream out("f1.dat",ios::out);
    if(!out)
    {
    	cerr<<"open error"<<endl;
    	exit(1);
    }
    for(int i=0;i<10;i++)
    {
    	cin>>a[i];
    	out<<a[i]<<" ";
    }
    out.close;
    
    \\输入文件
    iftstream in("f1.dat",ios::in);
    if(!in)
    {
    	cerr<<"open error"<<endl;
    	exit(1);
    }
    for(int i=0;i<10;i++)
    {
    	cin>>a[i];
    	cout<<a[i]<<" ";
    }
    in.close;
    
    \\fstream file("f1.dat",ios::out|ios::in); 既可以读又可以写
    
    while(in.get(ch)); //一个一个读取
    	cout<<ch;
    
  7. 对二进制文件的操作
    istream& read(char *buffer, int len);
    ostream& write(const char *buffer, int len);
    \\char *buffer指向内存中的一段存储控件。char是最小指针故用char(实际unsign char)。len是字符串
    调用方式
    ofstream a("file1.dat", ios::binary); //可以 | ios::in | ios::out
    ifstream b("file2.dat", ios::binary);
    a.write(p1,50);
    b.read(p2,30);
    
    \\一些输出方式
    for(int i =0;i<3;i++)
    	a.write((char*)&stud[i],sizeof(stud[i]));
    a.close;
    \\一些输入方式
    for(int i = 0;i<3;i++)
    	b.read((char*)&stud[i],sizeof(stud[i]));
    b.close;
    
    1. 二进制文件可以使用随机读写,可以使用文件指针(也是用创建的流对象名)移动到文件里的指定位置
    定义时可以用ios::beg等来定义位置
    file.seekg(3); 后移动3位
    
字符串流
  1. 字符串流也叫做缓冲区
  2. 无了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值