C++面试笔记--面向对象

说到面向对象,大家第一反应应该就是它的三大特性:封装性、继承性和多态性。那么我们先简单的了解一下这三大特性:

     (1)封装性:封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

在C++中类中成员的属性有:public, protected, private,这三个属性的访问权限依次降低。

     (2)继承性:继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

     (3)多态性:多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。

重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

  • 1、C++中空类默认产生哪些类成员函数?

    答案:

    对于一个空类,编译器默认产生4个成员函数:

    (1)默认构造函数

    (2)析构函数

    (3)拷贝构造函数

    (4)赋值函数

  • 2、结构是否可以有构造函数、析构函数及成员函数?如果可以,那么结构和类还有什么区别吗?

    答案:

    区别是class中变量默认是private,struct中的变量默认是public。class继承默认是private继承,而struct继承默认是public继承。struct可以有构造函数、析构函数,之间也可以继承甚至是多重继承,等等。C++中的struct其实和class意义一样,唯一不同就是struct里面默认的访问控制是public,class中默认访问控制是private。C++中存在struct关键字的唯一意义就是为了让C程序员有个归属感,是为了让C++编译器兼容以前用C开发的项目。

    下面我们写一段struct中继承的例子。

     1 #include<iostream>
     2 using namespace std;
     3 enum Bread{Golden,Cairn,Dandie,SheTland,Lab};
     4 
     5 struct mannal{
     6 public:
     7     mannal():itsAge(2),itsWeight(5){}
     8     ~mannal(){}
     9 
    10     int getage()const {return itsAge;}
    11     void setage(int age){itsAge=age;}
    12     int getweight()const {return itsWeight;}
    13     void setweight(int weight){itsWeight=weight;}
    14 
    15     void speak()const{cout<<"mannal sound!"<<endl;}
    16     void sleep()const {cout<<"sleeping!"<<endl;}
    17 
    18 protected:
    19     int itsAge;
    20     int itsWeight;
    21 };
    22 struct dog:public mannal{
    23 private:
    24     Bread itebread;
    25 
    26 public:
    27     dog():itebread(Golden){}
    28     ~dog(){}
    29     Bread getbread()const {return itebread;}
    30     void getbread(Bread bread){itebread=bread;}
    31     void wagtail()const {cout<<"wagging!"<<endl;}
    32     void begforfood()const{cout<<"food!"<<endl;}
    33 
    34 };
    35 int main(){
    36     dog fido;
    37     fido.speak();
    38     fido.wagtail();
    39     cout<<"fiod is  "<<fido.getage()<<"years old!"<<endl;
    40     return 0;
    41 }
  • 3、下面程序哪个语句是错的?

    struct test{
        test(int){}
        test(){}
        void fun(){}
    };
    int main()
    {
        test a(1);//语句一
        test b();//语句二
        a.fun();//
        b.fun();////答案选四,因为test b(),这个声明了一个函数,然而本意是想创建一个对象b,但是这个声明函数的错误在编译的时候是检测不出来的,所以就调用不了fun,所以四错误。
        
    }
  • 4、下面程序打印出的结果是什么?
    #include<iostream>
    using namespace std;
    class base
    {
    private:
        int m_i;
        int m_j;
    public:
        base( int i ) : m_j(i),m_i(m_j) {
        base() : m_j(0),m_i(m_j){}
        int get_i() {return m_i;}
       int get_j() {return m_j;}
    };
    int main ()
    {
        base obj(98);
        cout << obj.get_i() <<endl<< obj.get_j() <<endl;
        return 0;
    }

    解析:本题想得到的结果是“98,98”。但是成员变量的声明是先 m_i ,然后是 m_j;初始化列表的初始化变量顺序是根据成员变量的声明顺序来执行的,因此,先初始化 m_i,但此时 m_j 还未初始化,m_i 会被赋予一个随机值。改变一下成员变量的声明顺序可以得到预想的结果。

    答案:

    输出结果第一个为随机数,第二个是98。

  • 5、析构函数可以为 virtual 型,构造函数则不能,为什么?

    答案:

    虚函数采用一种虚调用的办法。虚调用是一种可以在只有部分信息的情况下工作的机制,特别允许我们调用一个只知道接口而不知道其准确对象类型的函数。但是如果要创建一个对象,你势必要知道对象的准确类型,因此构造函数不能为 virtual。

    6、如果虚函数是非常有效的,我们是否可以把每个函数都声明为虚函数?

    答案:

    不行,这是因为虚函数是有代价的:由于每个虚函数的对象都必须维护一个 v 表,因此在使用虚函数的时候会产生一个系统开销。如果仅是一个很小的类,且不行派生其他类,那么根本没必要使用虚函数。

  • 6、哪一种成员变量可以在同一类的实例间共享?

    答案:

    必须使用静态成员变量在一个类的所有实例间共享数据。如果想限制对静态成员变量的访问,必须把它们声明为保护型或私有型。不允许用静态成员去存放某一个对象的数据。静态成员函数是在这个类的所有对象间共享的。如果静态成员数据设为私有的,可以通过共有静态和产能原函数访问。

  • 7、编写类 String 的构造函数、析构函数和赋值函数。

       

    #include<iostream>
    using namespace std;
    class string{
       string(const char *str);
       string(const string &);
       ~string();
       string &operator=(const string &) ;
       private:
           char *m_data;
    };
    
    string::~string(){//析构函数
        delete []m_data;
    }
    string::string(const char *str){//构造函数,利用复制操作进行。
        if(NULL==str){
            m_data=new char[1];
            *m_data='';
        }
        else{
            int length=strlen(str);
            m_data=new char [length+1];
            strcpy(m_data,str);
        }
    }
    string::string(const string & a){
        int lenght=strlen(a.m_data);
        m_data=new char[length+1];
        strcpy(m_data,a,m_data);
    }
    string & string::operator=(const string &a){
        if(this==&other){//检查自赋值
            return *this;
        }
        delete []m_data;//释放原有的内存资源。
        int length=strlen(a.m_data);//分配新内存资源,并复制内容
        m_data=new char[length+1];
        strcpy(m_data,a.m_data);
        return *this;
    }
  • 8、重载与覆盖有什么不同?

    答案:

         虚函数总是在派生类中被改写,这种改写被称为“override”(覆盖)。

         override 是指派生类重写基类的虚函数,重写的函数必须有一致的参数表和返回值。Override这个单词好像一直没什么合适的中文词汇来对应。有些人译为“覆盖”,还贴切一些。

         overload约定成俗地被翻译为“重载”,是指编写一个与自己已有函数同名但是参数表不同的函数。例如一个函数既可以接受整型数作为参数,也可以接收浮点数作为参数。重载不是一种面向对象的编程,而是一种语法规则,重载与多态没什么直接关系。

  • 9、为什么析构函数可以是virtual,但是构造函数不行呢?

    答案:

    虚函数采用的是一种虚调用的方法。虚调用是一种可以在只有部分信息的情况下工作的机制,特别允许我们调用一个只知道借口而不知道准确对象类型的函数,但是如果要创建一个对象你势必要知道对象的准确类型,因此构造函数不能为虚。

  • 10、友元函数去实现两个点之间的距离,写一个poiny类

    答案:

     1 #include<iostream>
     2 using namespace std;
     3 class point{
     4 private:
     5     double x,y;
     6 public:
     7     point(double a=0.0,double b=0.0):x(a,b){}
     8     friend double distance(point &left,point &right){}
     9 };
    10 double distance(point &left,point &right){
    11     return ((left.x-right.x)^2+(left.y-right.y)^2)^0.5;
    12 }
  • 11、最好不要从析构函数中抛出异常!!!,构造函数中可以抛出异常,虚方法中也是可以抛出异常的。

    答案:

原文地址:https://www.cnblogs.com/Kobe10/p/5562936.html