多态性C++

#include <iostream>

class Person 
{
public:
    virtual void BuyTicket(int)
    {   
        std::cout << "Adult need Full Fare!" << std::endl;
    }   
};

class Child : public Person
{
public:
    virtual void BuyTicket(int)
    {   
        std::cout << "Child Free!" << std::endl;
    }   
};

void fun(Person& obj)
{
    obj.BuyTicket(1);                                                                                                                             
}

int main(void)
{
    Person p;
    Child c;

    fun(p);
    fun(c);

    return 0;
}

输出Adult need Full Fare!和Child Free!

调用函数就是这里的fun,参数int没有实际意义,就是为了体现函数重写必须要返回值一样、函数名一样和参数一样。
被调用的函数必须是虚函数,也就是说必须要在两个产生多态的函数前面加virtual关键字。
调用函数的形参对象必须是基类对象,这里是因为派生类只能给基类赋值,会发生切片操作。基类不能给派生类赋值。
调用函数的参数必须是指针或引用,因为派生类改变了虚表,那么这个虚表就属于派生类对象,赋值的时候只会把基类的成员给过去,虚表指针不会给。所以在调用函数的时候会发生语法检查,如果满足多态的条件,就会触发寻找虚表中虚函数地址。如果不满足条件,则会直接用基类对象调用基类函数。

不规范重写行为:
就是在派生类的重写函数加了virtual关键字,但是在派生类的重写函数前不加。
这样不会报错,因为继承的原因,将这个virtual的性质继承了下来,但是这样写不规范,
如果两个函数构成重写,那么要在两个函数前都加上virtual关键字。

2.析构函数重写问题
基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这也说明的基类的析构函数最好写成虚函数。

将析构函数定义为虚函数的原因:
因为基类指针可能指向派生类,当delete的时候,如果不定为虚函数,系统会直接调用基类的析构函数,这个时候派生类就有一部分没有被释放,就会造成可怕的内存泄漏问题。
若定义为虚函数构成多态,那么就会先调用派生类的析构函数然后派生类的析构函数会自动调用基类的析构函数,这个结果满足我们的本意。
所以!在继承的时候,尽量把基类的析构函数定义为虚函数,这样继承下去的派生类的析构函数也会被变成虚函数构成多态。

详细例子可见https://blog.csdn.net/skySongkran/article/details/82012698,重写不规范

虚继承

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

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

//直接基类C
class C: 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;
}

菱形继承,相同的只保留一份

static使用

static的作用主要有两种:

第一个作用是限定作用域;第二个作用是保持变量内容持久化;

c语言中static的用法:

1、全局静态变量:

  用法:在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。 static int temp;

  内存中的位置:静态存储区,在整个程序运行期间一直存在。

  初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);

  作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

2、局部静态变量:

  在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。

  内存中的位置:静态存储区

  初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);

  作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;

3、静态函数:

  在函数返回类型前加关键字static,函数就定义成静态函数。函数的定义和生命在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用;

c++中static的用法:

  1、类的静态成员:

  class A{

  private:

    static int val;

  };

  在cpp中必须对他进行初始化,初始化时使用作用域运算符来标明他所属类,其属于该类的所有成员共有,只有一个拷贝;

  2、类的静态成员函数:

  class A{

  private:

    static int func(int x);

  };

   实现的时候也不需要static的修饰,因为static是声明性关键字;类的静态函数是该类的范畴内的全局函数,不能访问类的私有成员,只能访问类的静态成员,不需要类的实例即可调用;实际上,他就是增加了类的访问权限的全局函数;

  void A::func(int);

  静态成员函数可以继承和覆盖,但无法是虚函数;

  3、只在cpp内有效的全局变量:

  在cpp文件的全局范围内声明:

  static int val = 0;

  这个变量的含义是该cpp内有效,但是其他的cpp文件不能访问这个变量;如果有两个cpp文件声明了同名的全局静态变量,那么他们实际上是独立的两个变量;

  4、只在cpp内有效的全局函数:

  函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;

  warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;

const使用

hashmap的hash冲突的几种解决办法

开放定址法:线性探测再散列等
链地址法:链表存储
再哈希:构建多个不同哈希函数
建立公共溢出区