C++复习!!!(课本:谭老爷爷版)

一、从C到C++


C++如何实现I/O?它的优势在哪里?

  • 1、c++中I/O的概念

    在c++中有一个stream,这个类,所有的I/O都以这个“流”为基础,包括我们认识的文件I/O。stream有两个重要的运算符:

    • a、插入器(<<):向流输入数据。
    • b、析取器(>>):从流中读取数据。
  • 2、与c语言相比之下的优势

    1、最大的好处时完成了对流操作的封装。原来文件操作都是一个个分开的函数,现在的操作都是对象的方法。
    2、stream适用于设备输入和输出的。fstream是stream的派生类,即文件流(设备对象是文件),作为文件在c++中的一个抽象对象。


什么是重载函数?如何区分重载函数?

  • 1、什么是重载函数?

    c++允许在同一作用域中用同意函数名来定义多个函数,这些函数的参数个数和参数类型不相同,这些同名的函数用来实现不同的功能,这就是函数的重载,即一个函数名多用

  • 2、区分函数重载
    • 1、函数形参的个数
    • 2、函数形参的类型
    • 3、形参的顺序
    • 4、不能把返回值作为函数重载的条件,原因是编辑器在编译时不回去判断函数的返回类型,函数只有调用后,编译器才会去验证返回类型,所以返回值类型不能做为函数重载的依据;
    • 5、常函数const可以作为函数重载的判断依据,原因:重载[]运算符时,有没有const的区别是:有const只读,没有const读写都可以。

什么是函数模板?为什么要使用函数模板?

  • 1、什么是函数模板?

    建立一个通用函数,其函数类型和形参不具体指定,用一个虚函数的类型来代表,这个通用的函数就称为函数模板

    例:max函数模板
    template<typename T>T max(T a, T b, T c) {}

  • 2、为什么要使用函数模板?

    简化对同名的知识形参类型不同的函数的定义。


使用带有默认参数的函数时,要注意些什么?

  • 1、如果函数的定义在函数调用之前,则应在函数定义中给出默认值。

  • 2、一个函数不能既作为重载函数,又作为有默认参数的函数。


什么是变量的引用?要注意什么?

  • 在c++中,变量的“引用”就是变量的别名,因此,引用又称别名
    建立“引用”的作用是为一个变量再起另一个名字,一边在需要时可以方便、间接地引用该变量。
    对一个变量的“引用”的所有的操作,实际上都是对其所代表的(原来的)变量的操作。

    例:有一个变量a,想给他起一个别名b

    1
    2
    int a;
    int &b = a;
  • ATTENTION:由于引用不是独立的变量,编译系统给它单独分配储存单元,因此,在建立引用时只有声明没有定义,只是声明它和原有某一变量的关系。


为什么要使用inline函数?

  • 为了提高效率

  • 调用函数时需要一定的时间,如果有些函数需要频繁使用,则累计所用时间会很长,从而降低程序的执行效率。
    c++提供一种提高效率的方法,即在编译时,将所调用函数的代码嵌入到主调函数中。这种嵌入到主调函数中的函数称为内联函数


作用域限定符“::”的作用是什么

  • 在c++中,不同作用域内声明的变量可以同名,当局部变量和全局变量同名时,在局部变量中引用全局变量用到作用域限定符“::”。
    当全局变量和局部变量名字相同,重载时,就规定:
    用 “:: + 全局变量名” 来称呼全局变量。


二、类和对象


程序 = 对象 + 对象

对象 = 算法 + 数据结构


什么是类?什么是对象?什么是抽象?什么是类的声明、类的定义?

  • 对象:

    客观世界中任何一个事物都可以看成一个对象(object)。

  • :

    类是所有面向对象语言的共同特征,所有面向对象的语言都提供了这种类型。

  • 抽象:

    对象的抽象,而对象是类的具体实例。

  • 数据抽象:

    只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程(设计)技术。

  • 类的声明:

    1
    2
    3
    4
    5
    6
    7
    8
    class 类名 {
    private:
    私有的数据和成员函数;
    public:
    公用的数据和成员函数;
    protected:
    受保护的数据和成员函数;
    };
  • 类的定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //1、先声明,后定义:
    //(1)、
    class 类名 对象名1,对象名2,...;
    //(2)、
    类名 对象名1,对象名2,...;

    //2、声明同时定义:
    class 类名 {
    private:
    私有的数据和成员函数;
    public:
    公用的数据和成员函数;
    protected:
    受保护的数据和成员函数;
    }对象名1,对象名2,...;

    //3、不出现类名直接定义对象(非常 不建议 使用):
    class {
    private:
    私有的数据和成员函数;
    public:
    公用的数据和成员函数;
    protected:
    受保护的数据和成员函数;
    }对象名1,对象名2,...;

c++中有哪些权限限定符?各起什么作用?

  • private

    private将成员声明为私有成员,只能被本类中的成员函数引用,类外不能调用(友元类除外)。

  • public

    public将成员声明为公有成员,既可以被本类成员函数所引用,也可以被类的作用域内的其他函数所引用。

  • protected

    protected将成员声明为受保护成员,不能被类外访问,但是可以被派生类的成员函数访问。


什么是类的封装?封装的原则是什么?

  • 1、什么是封装?

    封装可以隐藏实现细节,使代码模块化。
    封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。
    在面向对象编程上可理解为:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

  • 2、封装的原则是什么?

    简化用户接口,隐藏实现细节,这个是封装的根本目的。
    封装的难点在于如何设计接口。
    其一:必须保证接口是功能的全集,即接口能够覆盖所有需求。不能完成必要功能的封装是没有意义的。
    其二:尽量使接口是最小冗余的。这是为了简化类使用者的学习和使用,难用的接口是最容易让人遗忘的。冗余接口的存在是被允许的,但必须保证冗余接口是有效的。也就是说,增加冗余接口会带来很大的好处,比如性能的飞升。
    其三:要保证接口是稳定的,将接口和实现分离,并将实现隐藏,就是为了能保护类使用者在类的实现细节改变的情况下,不必随时修改。一旦接口被公布,永远也不要改变它。


同一个类所定义的不同对象在调用同一个成员函数时,调用的是同一段代码吗?通过什么来区分各自的数据?

  • 是同一段代码

  • 通过this指针:每一个成员函数中都包括这个指针的名字是固定的,称为this。它是指向对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址
    例:

    1
    2
    3
    4
    5
    6
    7
    int Box::volume(){
    return (height * width * length);
    }
    //C++把它处理为:
    int Box::volume(Box * this){
    return (this->height * this->width * this->length);
    }

类的成员函数和普通全局函数在形式上有什么区别?在本质上有什么区别?

  • 区别1:静态成员函数实际上是一个全局函数,不依赖一个类的对象. 而属于类,不创建对象也可调用,静态成员函数没有this指针。普通成员函数依赖一个类的对象,也就是它有一个隐藏的调用参数(this)指针,必须指向一个类的对象。

  • 区别2:静态函数只能访问类中的静态成员变量;

  • 区别3:如果成员函数想作为回调函数来使用,如创建线程等,一般只能将它定义为静态成员函数才行。


三、类和对象的进一步讨论


构造函数、析构函数的英文单词是什么?

  • 构造函数——constructor

  • 析构函数——destructor


构造函数的特点是什么?析构函数的特点是什么?

  • c++提供构造函数,来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。构造函数只是起初始化值的作用,但实例化一个对象的时候,可以通过实例去传递参数,从主函数传递到其他的函数里面,这样就使其他的函数里面有值了。规则,只要你一实例化对象,系统自动回调用一个构造函数,就算不写,编译器也自动调用一次。

  • 析构函数与构造函数同名,但该函数前面加~。析构函数没有参数,也没有返回值,而且不能重载,在一个类中只能有一个析构函数。当撤销对象时,编译器也会自动调用析构函数每一个类必须有一个析构函数,用户可以自定义析构函数,也可以是编译器自动生成默认的析构函数。一般析构函数定义为类的公有成员。


构造函数、析构函数的执行顺序是什么?

  • 先构造的先执行析构


在C语言中,我们说main()函数是程序的入口,是最先被调用的函数,在C++中情况是这样吗?如果不是请说明原因,并举出实例。

  • c++中不是这样

  • 因为存在构造数据类型的定义对象定义,且对象为全局对象的话,则该类对象所属的构造类型的构造函数在main()函数之前执行。


const关键字

const在c++中要尽可能的使用
形式 含义
Time const t1; t1是常对象,其值在任何情况下都不能改变
void Time::fun()const; fun是Time类中的成员函数,可以引用,但不能修改本类中的成员数据
Time * const p; p是指向time类对象的常指针变量,p的值(p的指向)不能改变
const Time * p; p是指向Time类常对象的指针变量,p指向的类对象的值不能通过p来改变
const Time &t1 = t; t1是Time类对象t的引用,二者指向同一储存空间,t的值不能改变
- > const的作用是:数据的保护
- 常成员变量

类的常成员变量是用const关键字声明的成员变量,其值不可改变,它的初始值必须通过构造函数的初始化列表方式完成。一般将一个类中不可改变的值声明为常成员变量。
但const成员变量有一个缺点:该类的所有对象都会const成员变量,而不是整个类共用一个。这种情况下,const常和static一起使用。

  • const成员函数
    const成员函数语法为:int Obj::Func const;这表明成员函数Func const是个常成员函数,它的作用是表明只能引用类中的数据成员,而不能修改类中的数据成员
  • 常对象
    语法为:const obj o(10);obj const o(10);
    用const声明的对象为常对象,常对象必须初始化,
    如果一个对象被声明为const对象,则该对象的数据成员不可修改。即const对象不能调用该对象的非const成员函数。
  • 指向const数据的指针
    定义指针时,在指针的类型前面加上const表明该指针指向的对象是个常量。
  • const指针
    在定义指针时,在指针的前面加上const表明该指针是一个常量,它必须被初始化。
  • const在参数中的使用
    const在参数类型为指针或引用时经常被使用,这表明这个指针或引用所指向的数据不允许被修改,这样就保证了在被调函数中不能因为误操作而改变了主调函数中的值。

初始化列表的语法是怎样的?

  • 如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    类名::构造函数名([参数表])[:成员初始化表]{
    [构造函数体]
    }
    //例:
    class Student{
    public:
    Student(int n, char s, nam[]):num(n), sex(s){
    strcpy(name, nam);
    }
    private:
    int num;
    char sex;
    int name[20];
    };

在C++中为什么使用new/delete,而不是malloc()/free()来来动态创建对象?如何使用new/delete来动态创建数组?

  • c语言中使用库函数malloc()和free()来动态申请和释放内存空间,但是存在两个问题:
    1、分配的大小需人为计算;
    2、只分配空间,而不涉及类型。
    c++使用了运算符new和delete来取代malloc()和free()函数。动态分配/释放内存

  • 例:动态创建一维数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
    using namespace std;
    int main(){
    int n;
    cin >> n;
    //动态分配一维数组
    int *arr = new int [n];
    for(int i = 0; i < n; i++)
    cin >> arr[i];
    for(int i = 0; i < n; i++)
    cout << arr[i] << " ";
    //释放arr数组
    delete[] arr;
    return 0;
    }

什么是对象的复制?什么是对象的赋值?

  • 对象的赋值

    一般形式:对象名1 = 对象名2;
    对象的赋值只是用赋值号右边的对象的数据覆盖了赋值号左边的的对象的数据成员。因此必须使用有默认参数值的构造函数,以保证左值能被初始化。

  • 对象的复制

    一般形式:类名 对象2(对象1); 或 `类名 对象2 = 对象1;
    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Student{
    pubilc:
    Student (){m_strName="Jim";}
    /*
    默认的拷贝构造函数:
    Student (const Student &stu){}
    */
    Student(const Student &stu){m_strName = stu.m_strName;}
    //显性定义的拷贝构造函数
    private:
    string m_strName;
    };
    int main(){
    Student stu1;
    Student stu2 = stu1;
    Student stu3(stu1);
    return 0;
    }

什么是深拷贝/浅拷贝? 拷贝构造函数的语法如何?

  • 浅拷贝

    对象拷贝时,仅仅是简单的值拷贝,这称为“浅拷贝”,这是系统默认的拷贝模式,不需要编程实现。

  • 深拷贝

    对象拷贝时,对堆中的数据编程进行拷贝,这称为“深拷贝”,这种拷贝模式需要编程实现。

  • 拷贝构造函数

    拷贝构造函数中只有哟个参数,为本类对象的一个引用,并通常使用const修饰。
    语法:

    1
    2
    3
    4
    obj(const obj &o){
    no = o.no;
    cout << "copy constructor" << no << endl;
    }

static数据成员和static成员函数的特点是什么?static成员函数有什么限制?static数据成员如何初始化?

  • static数据成员拥有一块单独的储存区(不再类对象内部),不管该类创造了多少个对象。所有该类的对象共享这块区域,这就为这些类之间提供了一种相互通信的方法,并且它不会占据对象的储存空间。他的名字是属于类的(例:Circle::PI),这样可以很大程度上解决全局数据的名字冲突问题。它是有权限控制的,也就是说他可以是private的。

  • 除了数据成员能定义为static的,成员函数也可以定义为static的。与static数据成员一样,static成员函数也是属于类的,而不是属于对象的。static成员函数是属于类的,所以它没有this指针,不创建对象就可以被调用。它的这个特点决定了static成员函数只能访问类中的static数据成员和static成员函数。与类的static数据成员一样,推荐使用类方式引用类的static成员函数

  • 在类内提供的仅仅是static数据成员的声明(编译器没有给它分配空间),而不是定义,所以不能使用构造函数对其进行初始化,static数据成员的初始化必须在类外完成。static数据成员在使用时,既可以使用对象方式引用,也可以使用类方式引用,通常使用类方式引用,以表明它是一个static数据成员。


为什么要使用友元?哪些内容可以作为类的友元?我们对友元的态度是什么?

  • 友元机制可以带来一些便利。

  • 友元可以实现在类的外部访问一个类的private成员

  • 友元机制虽然可以带来一些便利,但是它严重破坏了程序的封装性,也有影响程序的可读性,所以要慎重使用友元


四、运算符重载


在C++中运算符被当作什么处理?如何重载运算符?重载运算符有什么好处?需要注意哪些问题?运算符重载一般以哪两种方式出现?该如何选择?


五、继承和派生


继承的单词是什么?语法如何?继承方式限定符有哪些?作用分别是什么?

  • 继承——inheritance。继承就是在已有类的基础上建立一个新类的过程。其中已经存在的类称为基类(base class),新建立的类称为派生类(derived class),从基类产生派生类的过程称为派生。

  • 继承的语法:

    1
    2
    3
    4
    class 派生类名: [继承方式] 基类名 {
    //继承方式不写默认是private。
    派生类新增加的成员
    }
  • 继承方式限定:
    private:基类的公有成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。
    public:积累的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。
    protected:积累的公有成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有。


继承发生时,在派生类的对象内部会有一个基类的对象,这个基类对象的初始化是由基类的构造函数完成的,那么如何在派生类中有选择的调用基类的非默认构造函数呢?


继承发生时,派生类是否全部接收基类的成员?如果不是哪些成员不被继承。

  • 派生类要无条件的接收基类中的全部成员

  • 派生类尽管拥有基类的所有的成员,但是派生类不能访问基类private的成员


派生类从基类中继承的成员在不同的继承方式下的访问权限

![](https://hdwena.sn.files.1drv.com/y4mhMErgzce0OjomgHTh_UJda9Dha1RoPDfRdvbQdw6z2vtiulYKo4l89RC8FBLsUEN7NHN0I8-RcpUKyyim93Gtb1ckcUYLDxQnIAAQvIIK2HAC0YOZshMXXpz2INa5S_lq88gY0d1bk8i6uIlXBWPip1S_e2rKV7CueKh7Ykj4faOsqIkFOO4XqpMoESSDg7r3I6OMZTvX4FfIH7X5mQawA?width=944&height=299&cropmode=none)

组合(子对象)的作用是什么?如何有选择的调用对象成员的非默认构造函数?组合和继承都可以实现代码重用,那么什么时候选择组合,什么时候选择继承?

  • 子对象就是类对象中内嵌的对象。当派生类中存在子对象时,派生类的构造函数需要依次完成以下3部分:
    1、先调用基类构造函数初始化基类数据。
    2、再调用子对象的构造函数初始化子对象中的数据。
    3、最后初始化自己的数据成员。


当继承和组合都存在时,构造和析构的顺序如何?


哪些地方必须使用初始化列表?


什么是overriding、overload?什么是upcasting?


多继承会带来什么问题?如何解决这个问题?对待多继承的态度是什么?


六、多态性和虚函数


什么是多态性?英文单词是什么?它能给我们程序设计带来什么好处?


如何实现多态性?


虚函数是如何实现的(考试不会考)?


动态/静态绑定(binding、联编)的优缺点各是什么?


什么是纯虚函数?什么是抽象类?其意义各自是什么?


七、输入输出流


C++I/O的优势在哪(和前面第一章的类似)?


标准输出流有哪些?它们之间的区别是什么?如何控制格式的输出?标准的输入流是什么?


如何使用文件流进行输入/输出(步骤)?


什么是字符串流?它有什么用好处?


八、C++工具


实现异常的三个关键字是什么?大致的作用是什么?


AbelChe wechat
扫码加微信
Donate here!!!
0%