一、简单题

  1. 有哪些方法可以让函数返回多个值

    • 利用全局变量
    • 利用C++引用
    • 传递数组指针
    • 传递结构体指针
  2. 派生新类的过程包括哪三个步骤

    吸收基类成员、改造基类成员、添加新的成员

  3. 派生类构造函数的执行顺序

    • 先调用基类构造函数,调用顺序按照他们被继承时声明的顺序(从左向右)

    • 然后对派生类新增的成员对象初始化,初始化顺序按照他们在类中的声明顺序

    • 最后执行派生类自己的构造函数的内容

      析构函数的调用顺序与构造函数的调用顺序恰恰相反

  4. 函数对象

    重载函数调用操作符的类,其对象称为函数对象,即它们是行为类似函数的对象

    函数对象其实就是一个行为类似函数的对象,它可以不需要参数,也可以带有若干参数,其功能是获取一个值,或者改变操作的状态。

  5. 类模块

    类模板可以为类定义一种模式,使得类中的某些数据成员、成员函数的参数、返回值或局部变量能取任意类型。

  6. 封装

    封装就是将抽象的到的数据和行为相结合,形成一个有机的整体,也就是将数据与操作数据的 函数代码进行有机的结合,形成类

  7. 动态绑定

    绑定工作在程序运行阶段完成的情况称为动态绑定,也称为晚绑定或后绑定

  8. 容器

    容器实质上是一组相同类型对象的集合,它不仅仅是数组那么简单,它实现了比数组更复杂的数据结构,能够实现更复杂的功能,可以持有其他对象或指向其他对象的指针。

  9. 面向对象的四个基本特性

    抽象性、多态性、继承性、封装性

  10. 友元函数、全局函数、成员函数的不同

    成员函数定义在类内部,作用域在类的内部,可以访问类的所有数据成员和成员函数。

    不是在类中定义的成员函数都是全局函数,全局函数只能访问类中的公有数据成员。

    如果某一个函数在另一个类中用friend声明,就是另一个类的友元函数,友元函数可以访问对应类的私有成员。

  11. 如果没有自定义拷贝构造函数,系统会自动调用默认拷贝构造函数,会发生什么现象

    默认拷贝构造函数一般情况下是够用的,因为它实现的是对象间数据元素的一一对应复制,但是当类的数据成员中含有指针调用默认的拷贝构造函数就会出现问题,默认拷贝构造函数会造成浅复制,两个对象的指针成员实际上是指向同一个内存地址,当两个对象被析构的时候,会导致同一内存地址被释放两次,造成内存泄露。

  12. 面向对象程序设计和结构化程序设计如何实现“分治与递归”

    面向对象程序设计是把构成问题的事务分解成各个对象,每个对象有自己的数据成员和函数成员,每个对象都有自己的“分治与递归”的策略

    结构化程序设计是面向过程的,由一个个具体的函数组成,为了实现“分治与递归”,需要编写递归函数

  13. C++异常处理机制

    异常处理是C++的一项语言机制,用于在程序中处理异常事件。异常事件在C++中表示为异常对象。异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统为程序设置当前异常对象,然后执行程序的当前异常处理代码块,在包含了异常出现点的最内层的try块,依次匹配catch语句中的异常对象(只进行类型匹配,catch参数有时在catch语句中并不会使用到)。若匹配成功,则执行catch块内的异常处理语句,然后接着执行try…catch…块之后的代码。如果在当前的try…catch…块内找不到匹配该异常对象的catch语句,则由更外层的try…catch…块来处理该异常;如果当前函数内所有的try…catch…块都不能匹配该异常,则递归回退到调用栈的上一层去处理该异常。如果一直退到主函数main()都不能处理该异常,则调用系统函数terminate()终止程序。

  14. 什么是拷贝构造函数,自定义拷贝构造函数有什么用

    拷贝构造函数又称复制构造函数,是一种特殊的构造函数,其形参是本类对象的引用,其作用是使用一个已经存在的对象去初始化同类的一个新对象。系统默认复制构造函数实现的是对应对象间数据元素的一一对应复制,实现的是浅拷贝。如果数据元素中有指针成员,会导致不同的指针指向相同的内存,在对象析构时同一内存回收两次,导致出错。自定义拷贝构造函数在遇到指针对象时,会申请新的内存空间,实现的是深拷贝。

  15. 什么时候会自动调用复制构造函数

    (1)当用类的一个对象去初始化该类的另一个对象时

    (2)当函数的形参是类的对象,调用函数时,进行形参和实参结合时

    (3)当函数的返回值是类的对象

  16. 简述类和对象的关系及区别

    类是具有相同属性和服务的一组对象的集合。对象是系统中用来描述客观事物的一个实体。类是对象的抽象,对象是类的具体实例。一个类可以有多个对象,每个对象都有自己的存储单元,类不占存储单元。

  17. 有什么方法可以实现代码重用

    (1)模板:将不同的对象的类型作为模板参数,在使用模板时只需要改变模板参数就能达到代码重用的功能

    (2)使用宏定义:将不同类型的对象作为宏参数,

    (3)继承与派生:通过公有继承,派生类可以使用基类的公有函数成员,从而达到代码的重用

  18. 静态绑定与动态绑定的相同点与不同点

    静态绑定:指的是绑定工作在编译连接阶段完成的情况

    动态绑定:指的是绑定工作在程序运行阶段完成的情况

    相同点:都需要确定同名操作的具体操作对象(即绑定),需要将一个消息和一个对象的方法结合起来

    不同点:绑定的时间不多,静态绑定是在编译连接阶段完成的,动态绑定是在程序运行阶段完成的

  19. 解释public、protected、private的意义和用途

    (1)意义:通过public、protected、private关键字,可以实现对类成员访问权限的控制;同时在继承、派生体系中,通过public、protected、private关键字,可以实现对基类的访问权限控制

    (2)在类成员的访问控制中,类外可以访问public关键字声明的公有成员。在继承派生中,public继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。

    在类成员的访问控制中,private关键字声明的成员只能在类中进行法访问,来自类外的任何访问都是非法的。在继承派生中,private继承时,基类中的公有成员和保护成员都以私有成员的身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。

    在类成员的访问控制中,protected关键字声明的保护成员在类外无法访问,但是在派生类中可以访问。在继承派生中,protected继承时,基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。

二、程序设计题

  1. 定义一个处理日期的类TDate,三个私有成员,Month,Day,Year,定义一个友元函数打印日期,定义一个非静态成员函数设置日期

    #include <iostream>
    using namespace std;
    class TDate {
    private:
    int year;
    int month;
    int day;
    public:
    TDate(){
    year = 0;
    month = 0;
    day = 0;
    }
    TDate(int year,int month,int day) {
    this->year = year;
    this->month = month;
    this->day = day;
    }
    void setTime(int year = 2020,int month = 1,int day = 1);
    friend void printDate(TDate &date);
    };
    void TDate::setTime(int year, int month, int day) {
    this->year = year;
    this->month = month;
    this->day = day;
    }
    void printDate(TDate &date){
    cout << date.year << "年" << date.month << "月" << date.day << "日" << endl;
    }
    int main() {
    TDate date;
    date.setTime(2020,1,1);
    printDate(date);
    return 0;
    }
  2. 定义一个堆栈类模块,并写出测试程序

    #include <iostream>
    #include <cassert>
    using namespace std;
    template <class T, int SIZE = 50>
    class Stack {
    private:
    T list[SIZE];
    int top;
    public:
    Stack();
    void push(const T &item);
    T pop();
    void clear();
    const T &peek() const;
    bool isEmpty() const;
    bool isFull() const;
    };
    template <class T, int SIZE>
    Stack<T,SIZE>::Stack():top(-1) {}
    template <class T, int SIZE>
    void Stack<T,SIZE>::push(const T &item) {
    assert(!isFull());
    list[++top] = item;
    }
    template <class T, int SIZE>
    T Stack<T,SIZE>::pop() {
    assert(!isEmpty());
    return list[top--];
    }
    template <class T, int SIZE>
    void Stack<T,SIZE>::clear() {
    top = -1;
    }
    template <class T, int SIZE>
    const T &Stack<T,SIZE>::peek() const {
    assert(!isEmpty());
    return list[top];
    }
    template <class T, int SIZE>
    bool Stack<T,SIZE>::isFull() const {
    return top == SIZE - 1;
    }
    template <class T, int SIZE>
    bool Stack<T,SIZE>::isEmpty() const {
    return top == -1;
    }
    int main() {
    Stack<int> s1;
    int i;
    for (i = 0; i < 50; ++i) {
    s1.push(i);
    }
    cout << "连续进栈50次后,栈顶元素为:" << s1.peek() << endl;
    cout << "栈是否为满:" << s1.isFull() << endl;
    return 0;
    }
  3. 实现MyString类

    #include <iostream>
    using namespace std;
    class Mystring {
    private:
    char *s;
    public:
    Mystring();
    Mystring(const char *pstr);
    Mystring(Mystring &rhs);//复制构造函数
    ~Mystring();//析构函数
    Mystring& operator=(const Mystring &rhs);
    Mystring operator+(const Mystring &rhs);
    friend ostream& operator<<(ostream &os, const Mystring &rhs);
    };
    Mystring::Mystring():s(new char[1]()) {}
    Mystring::Mystring(const char *pstr) {
    if(pstr == nullptr) {
    s = new char[1]();
    s[0] = '\0';
    } else {
    s = new char[strlen(pstr)+1];
    strcpy(s,pstr);
    }
    }
    Mystring::Mystring(Mystring &rhs) {
    s = new char[strlen(rhs.s)+1];
    strcpy(s,rhs.s);
    }
    Mystring::~Mystring() {
    delete[] s;
    }
    Mystring & Mystring::operator=(const Mystring &rhs) {
    if(this == &rhs) {
    return *this;
    }
    delete[] s;
    s = new char[strlen(rhs.s)+1];
    strcpy(s,rhs.s);
    return *this;
    }
    Mystring Mystring::operator+(const Mystring &rhs) {
    Mystring temp;
    delete[] temp.s;
    int len = strlen(rhs.s) + strlen(s) + 1;
    temp.s = new char[len];
    strcpy(temp.s,s);
    strcat(temp.s,rhs.s);
    return temp;
    }
    ostream& operator<<(ostream &os, const Mystring &rhs) {
    os << rhs.s;
    return os;
    }
    int main() {
    Mystring s1;
    Mystring s2("hello"), s4(" world");
    Mystring s3(s2);
    s1 = s2;
    s2 = s1 + s4;
    cout << s2;
    return 0;
    }
  4. 构造一个类MyARR数组

    #include <iostream>
    using namespace std;
    class MyARR {
    private:
    int *arr;
    int n;
    public:
    MyARR(){
    arr = nullptr;
    n = 0;
    }
    MyARR(int *b, int n) {
    if(b == nullptr) {
    MyARR();
    } else {
    this->n = n;
    arr = new int[n];
    for (int i = 0; i < n; ++i) {
    arr[i] = b[i];
    }
    }
    }
    ~MyARR(){
    delete[] arr;
    }
    void deleteSame();
    void show() const;
    };
    void deleteArr(int *a, int pos, int len) {
    for (int i = pos; i < len; ++i) {
    a[i] = a[i+1];
    }
    }
    void MyARR::deleteSame() {
    for (int i = 0; i < n; ++i) {
    int temp = arr[i];
    for (int j = i+1; j < n; ++j) {
    if(arr[j] == temp) {
    deleteArr(arr,j,n);
    j = i;
    n--;
    }
    }
    }
    }
    void MyARR::show() const {
    for (int i = 0; i < n; ++i) {
    cout << arr[i];
    if(i != n-1) cout << " ";
    }
    cout << endl;
    }
    int main() {
    int a[16] = {1,2,2,2,3,4,5,5,5,6,6,6,7,8,9,10};
    MyARR b(a,16);
    b.show();
    b.deleteSame();
    b.show();
    return 0;
    }
  5. 实现一个单链表,并用高效的算法实现查找链表中倒数第k个元素

    struct Linknode {
    int data;
    struct Linknode *next;
    };
    int search_k(Linknode *list, int k) {
    Linknode *p = list->next, *q = list->next;
    int count = 0;
    while(p != NULL) {
    if(count < k) {
    count++;
    } else {
    q = q->next;
    }
    p = p->next;
    }
    if(count < k) {
    printf("结点数不足!\n");
    return 0;
    } else {
    printf("%d\n",q->data);
    return 1;
    }
    }
  6. 创建一个抽象类Stack,分别用顺序结构和链式结构来实现。要求栈类的元素可以是任意类型

    #include <iostream>
    using namespace std;
    const int MAXSIZE = 50;
    template <class T>
    class Stack {
    public:
    Stack() = default;
    ~Stack() = default;
    virtual void push(const T &item) = 0;
    virtual T pop() = 0;
    virtual void clear() = 0;
    virtual const T& peek() const = 0;
    virtual bool isEmpty() const = 0;
    virtual bool isFull() const = 0;
    };
    template <class T>
    class Sequence_Stack:public Stack<T> {
    private:
    int top;
    T list[MAXSIZE];
    private:
    Sequence_Stack();
    ~Sequence_Stack();
    virtual void push(const T&item);
    virtual T pop();
    virtual void clear();
    virtual const T& peek() const;
    virtual bool isEmpty() const;
    virtual bool isFull() const;
    };
    template <class T>
    Sequence_Stack<T>::Sequence_Stack(){
    top = -1;
    }
    template <class T>
    Sequence_Stack<T>::~Sequence_Stack(){
    delete[] list;
    }
    template <class T>
    void Sequence_Stack<T>::push(const T &item){
    assert(!isFull());
    list[++top] = item;
    }
    template <class T>
    T Sequence_Stack<T>::pop(){
    assert(!isEmpty());
    return list[top--];
    }
    template <class T>
    void Sequence_Stack<T>::clear(){
    top = -1;
    }
    template <class T>
    const T& Sequence_Stack<T>::peek() const {
    assert(!isEmpty());
    return list[top];
    }
    template <class T>
    bool Sequence_Stack<T>::isEmpty() const{
    return top == -1;
    }
    template <class T>
    bool Sequence_Stack<T>::isFull() const{
    return top == MAXSIZE-1;
    }
    template <class T>
    class node {
    public:
    T data;
    node<T> *next;
    };
    template <class T>
    class Chain_Stack:public Stack<T> {
    private:
    node<T> *list;
    int size;
    public:
    Chain_Stack();
    ~Chain_Stack();
    virtual void push(const T &item);
    virtual T pop();
    virtual void clear();
    virtual const T &peek() const;
    virtual bool isEmpty() const;
    virtual bool isFull() const;
    };
    template <class T>
    Chain_Stack<T>::Chain_Stack() {
    list = new node<T>;
    list->next = nullptr;
    size = 0;
    }
    template <class T>
    Chain_Stack<T>::~Chain_Stack() {
    node<T> *p = list->next;
    list->next = nullptr;
    node<T> *temp;
    while(p) {
    temp = p;
    p = p->next;
    delete temp;
    }
    delete list;
    }
    template <class T>
    void Chain_Stack<T>::push(const T &item) {
    assert(!isFull());
    node<T> *newNode = new node<T>();
    newNode->data = item;
    newNode->next = list->next;
    list->next = newNode;
    size++;
    }
    template <class T>
    T Chain_Stack<T>::pop() {
    assert(!isEmpty());
    node<T> *temp = list->next;
    list->next = temp->next;
    size--;
    T t = temp->data;
    delete temp;
    return t;
    }
    template <class T>
    void Chain_Stack<T>::clear() {
    node<T> *p = list->next;
    list->next = nullptr;
    node<T> *temp;
    while(p) {
    temp = p;
    p = p->next;
    delete temp;
    }
    size = 0;
    }
    template <class T>
    const T& Chain_Stack<T>::peek() const {
    assert(!isEmpty());
    return list->next->data;
    }
    template <class T>
    bool Chain_Stack<T>::isEmpty() const {
    return size == 0;
    }
    template <class T>
    bool Chain_Stack<T>::isFull() const {
    return size == MAXSIZE;
    }
  7. 给出抽象类Shape,派生Rectangle和Circle,求周长和面积

    #include <iostream>
    using namespace std;
    class Shape {
    public:
    virtual double GetPrime() const = 0;
    virtual double GetArea() const = 0;
    };
    class Rectangle:public Shape {
    private:
    double x,y;
    public:
    Rectangle(double x = 0, double y = 0) {
    this->x = x;
    this->y = y;
    }
    virtual double GetPrime() const {
    cout << "The perime of Rectangle is" << 2*(x+y) << endl;
    return 2*(x+y);
    }
    virtual double GetArea() const {
    cout << "The area of Rectangle is" << x*y << endl;
    return x*y;
    }
    };
    class Circle:public Shape {
    private:
    double r;
    public:
    Circle(double r = 0) {
    this->r = r;
    }
    virtual double GetPrime() const {
    cout << "The perime of Circle is" << 2*3.14*r << endl;
    return 2*3.14*r;
    }
    virtual double GetArea() const {
    cout << "The area of Circle is" << 3.14*r*r << endl;
    return 3.14*r*r;
    }
    };
    void getPrimeAndArea(Shape *s) {
    s->GetArea();
    s->GetPrime();
    }
    int main() {
    Rectangle r1(3,4);
    Circle c1(2);
    getPrimeAndArea(&r1);
    getPrimeAndArea(&c1);
    return 0;
    }
  8. 实现一个ArrayList,要求实现构造函数、析构函数以及重载下标运算符和输出<<运算符

    #include <iostream>
    using namespace std;
    template <class T>
    class ArrayList {
    private:
    int length;
    T *list;
    public:
    ArrayList(){
    list = NULL;
    length = 0;
    };
    ArrayList(int len) {
    if(len <= 0) {
    this->length = 0;
    return;
    }
    this->length = len;
    list = new T(length);
    }
    ArrayList(ArrayList &obj) {
    if(obj.length > 0) {
    length = obj.length;
    list = new T(length);
    for (int i = 0; i < length; ++i) {
    list[i] = obj[i];
    }
    }
    }
    int getLen() {
    return this->length;
    }
    void setData(int index, int value) {
    if(list != NULL && index >= 0) {
    list[index] = value;
    }
    }
    int getData(int index) {
    if(index >= 0) {
    return list[index];
    }
    }
    int &operator[](int i) {
    return list[i];
    }
    ArrayList &operator=(ArrayList &obj) {
    if(list != NULL) {
    delete[] list;
    length = 0;
    }
    length = obj.length;
    list = new T[length];
    for (int i = 0; i < length; ++i) {
    list[i] = obj[i];
    }
    return *this;
    }
    bool operator==(ArrayList &obj) {
    if (length != obj.length) {
    return false;
    }
    for (int i = 0; i < length; ++i) {
    if(list[i] != obj[i]) {
    return false;
    }
    }
    return true;
    }
    friend ostream &operator<<(ostream &os, const ArrayList<T> &obj) {
    for (int i = 0; i < obj.length; ++i) {
    os << obj.list[i];
    if (i != obj.length-1) os << " ";
    }
    os << endl;
    return os;
    }
    };
    int main() {
    ArrayList<int> list(10);
    list.setData(0,1);
    list.setData(1,2);
    cout << list;
    return 0;
    }
  9. s