1.继承会将所有的成员继承下来,但是继承方式限定的是继承下来成员的可见类型(如果是private继承,那么他不论哪里都是不可见的;如果是protected继承在类中是可见的,在类外是不可见的;如果是public继承,在任何地方都是可见的)
2.对于静态成员变量,不计入字节大小
3.虚函数类外定义时,不必加virtual
4.编译时多态——静态多态——模板和重载
运行时多态——动态多态——虚函数
友元函数 构造函数 static静态函数 不能用virtual关键字修饰;
普通成员函数 和析构函数 可以用virtual关键字修饰;
静态成员变量类内声明类外初始化
静态成员变量为什么不能设置为虚函数:
静态成员函数不属于任何成员,属于整个类,不能使用this来访问
virtual构成的虚函数,恰恰是使用this指针访问,this->vfptr-> 虚函数地址
静态成员函数没有this指针,实现多态就是需要不同的对象,调用不同的子类进行访问不同的重写函数
class A
{
static int _tem;//静态成员变量类内声明,类外初始化
};
int A::_tem = 0;//在类外初始化的时候不加static
静态成员变量会继承使用权,但是不能被包含
静态成员变量不存储在类中,计算类的大小的时候不计算他的字节大小
重载:同一个作用域中,函数名相同,参数类型不同,参数数量不同,参数顺序不同
重定义(隐藏):继承中,函数名相同(可使用作用域进行访问)
重写(覆盖):继承的虚函数中,子类重写父类的虚函数
派生类的构造函数,不写会调用默认的构造函数,但是如果自己写,就要自己调用父类的构造函数(在调用父类的构造函数时,要将父类当成一个整体)
class Base
{
public:
Base()
{
puts("Base()");
}
Base(int a):_a(a)
{
puts("Base(int a)");
}
Base(const Base& tem)
{
puts("Base(const Base& tem)");
}
Base& operator=(const Base& tem)
{
puts("Base& operator=(const Base& tem)");
_a = tem._a;
return *this;
}
~Base()
{
puts("Base");
}
private:
int _a;
};
class Son:public Base
{
public:
Son()
{
puts("Son()");
}
Son(int a, int b)
:Base(a),_b(b)
{
puts("Son(int a,int b)");
}
Son(const Son& tem)
:Base(tem)
{
//父类接受子类的对象/引用/指针
puts("Son(const Son& tem)");
}
Son& operator=(const Son& tem)
{
puts("Son& operator=(const Son& tem)");
Base::operator=(tem);
return *this;
}
~Son()
{
//在析构函数的时候,可以不手动调用析构函数
//编译器会自动调用父类的析构函数
puts("~Son()");
}
private:
int _b;
};
inline能不能是虚函数:可以(内敛函数没有地址)
多态调用:内敛不起作用(多态调用中,虚函数存在虚函数表中,需要地址,但是内联函数没有地址,所以内敛不起作用)
普通调用:内敛起作用
构造函数能不能是虚函数?不能
虚表是在编译时生成,构造的时候进行初始化
如果构造函数是虚函数,那么在实例化对象的时候,如何应该去虚表中找构造函数的地址,但是这时候虚表还没有初始化
多态调用和普通函数的时间效率?
具体要看是否构成多态,调用需要到需表中找地址进行调用,普通成员函数可以直接调用
多态的本质——虚表
当父类的指针/引用接收子类的地址/对象时,因为是继承会进行切割,将子类的父类那部分切出来,剩下的就是去虚表中找地址进行调用就行了
父类=子类;会将父类的那部分切出来拷贝给父类,但是不会拷贝虚函数表指针
虚函数指针如果进行了拷贝,那么父类对象的虚函数表指针会发生改变,当使用父类对象调用父类的虚函数时,就会发生错误
虚函数继承,继承的是接口,参数类型不会改变;当子类对象要调用父类的函数时,使用切片的手法进行调用
普通函数继承,继承的是实现
子类和父类都有虚函数,子类的虚函数会存到哪里
从右表中可以看到,应该是有三个虚函数,他们在同一个虚表中
子类中没有虚表,子类的虚函数存到父类的虚表中
可以记成向上合并