C++基础知识(2)

8、什么是野指针?

野指针就是指向一个已经删除或者未申请访问受限的内存区域的指针。

9、什么是函数指针?

函数指针是指向函数的指针变量。函数指针本身是一个指针变量,该指针变量指向一个具体的函数。每个函数都有一个入口地址,该入口地址就是函数指针指向的地址,可以通过函数指针调用所指向的函数。

eg:

char* fun(char* p){...}  // 函数fun
char* (*pf)(char* p);  // 函数指针pf
pf = fun;   // 函数指针pf指向函数fun
pf(p);  // 通过函数指针pf调用函数fun 

10、为什么析构函数必须是虚函数?为什么C++默认的析构函数不是虚函数?

(1)对于可能会被继承的父类的析构函数设置为虚函数,可以保证当父类指针指向子类对象时,释放父类指针可以释放子类的内存空间,防止内存泄漏。

(2)C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚函数指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有需要当作父类时,才会把析构函数设为虚函数。

11、C++中析构函数的作用

(1)当对象结束其生命周期时,系统会自动执行析构函数,析构函数没有参数和返回值,也不能重载。

(2)如果用户没有编写析构函数,那么系统会自动合成一个析构函数,如果自定义了析构函数,那么编译器会先执行自定义的析构函数再执行合成的析构函数。

(3)如果一个类中有指针,且在使用过程中动态申请了内存,那么最好显式地构造一个析构函数,在销毁类之前释放掉所申请的内存空间,避免内存泄漏。

(4)类的析构顺序,先调用派生类本身的析构函数,再调用对象成员析构函数,最后调用基类析构函数。

12、虚函数与纯虚函数以及虚函数表之间的关系

(1)简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。C++中虚函数的作用主要是实现多态机制。所谓多态就是用父类指针指向子类对象,然后通过父类指针调用实际子类的成员函数,这种技术可以让父类指针有“多种形态”。

(2)纯虚函数,在虚函数形参后面写上=0,则该函数为纯虚函数。纯虚函数没有函数体;纯虚函数只有函数的名字而不具备函数的功能,不能被调用。纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对他进行定义。如果在一个类中声明了纯虚函数,在其派生类中没有对其函数进行定义,则该虚函数在派生类中仍然为纯虚函数。

(3)虚函数是通过虚函数表来实现的,在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题。在有虚函数的实例中这张表被分配在这个实例的内存中,所以当我们用父类指针来操作一个子类的时候,这张虚函数表就像地图一样指明实际所应该调用的函数。

另外,需要注意的是,当父类指针指向子类对象时,不能访问子类特有的虚函数,只能访问子类覆盖父类的虚函数。(详细可以看虚函数与纯虚函数以及虚函数表之间的关系

13、什么是虚继承?

(1)多继承容易产生冲突,即便小心翼翼给所有类的成员变量和成员函数命名为不同的类,冲突依旧可能会发生,比如典型的菱形继承。而虚继承的产生就是为了就是解决多继承冲突的问题。

(2)假如类A派生出类B和类C,而类D继承自类B和类C,那么A就是D的间接基类,而B和C就是C的直接基类。假如类A中有一个成员变量m_a,那么类D直接访问m_a会产生冲突,因为编译器并不知道m_a是来自A-->B-->D这条路径还是来自A-->C-->D这条路径,产生二义性。为了消除这种歧义可以在m_a前面加上作用域指明它是来自哪个类的,比如B::m_a或者C::m_a。

(3)为了解决多继承时的命名冲突和数据冗余的问题,C++提出了虚继承的概念,使得派生类只保留一份间接基类的成员,在继承方式加上virtual关键字就是虚继承。这样类B和类C都是类A虚派生出来的,那么类D只会拥有一份类A的成员数据,不会再产生二义性,此时类A又称为虚基类。

eg:

//间接基类A
class A{
protected:
    int m_a;
};

//直接基类B
class B: virtual public A{  //虚继承
protected:
    int m_b;
};

//直接基类C
class C: virtual public A{  //虚继承
protected:
    int m_c;
};

//派生类D
class D: public B, public C{
public:
    void seta(int a){ m_a = a; }  //正确
    void setb(int b){ m_b = b; }  //正确
    void setc(int c){ m_c = c; }  //正确
    void setd(int d){ m_d = d; }  //正确
private:
    int m_d;
};

int main(){
    D d;
    return 0;
}

(4)C++标准库中的 iostream 类就是一个虚继承的实际应用案例。iostream 从 istream 和 ostream 直接继承而来,而 istream 和 ostream 又都继承自一个共同的名为 base_ios 的类,是典型的菱形继承。此时 istream 和 ostream 必须采用虚继承,否则将导致 iostream 类中保留两份 base_ios 类的成员。

14、什么是静态多态和动态多态?

多态可以简单概括为”一个接口,多种方法“

(1)静态多态

静态多态也称为期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型出所要调用的那个函数。静态多态通常有两种实现方式:函数重载以及函数模板。

(2)动态多态

动态多态即运行时的多态,在程序执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。动态多态通常使用虚函数实现,通过基类引用或者指针指向子类对象时,根据运行时指针或引用实际指向的类型确定,如果调用子类覆盖父类的虚函数时,那么调用的是子类的虚函数,否则调用的是基类的函数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

czy1219

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值