一、继承:
使用已经编写好的类来创建新类,新的类具有原有类的所有属性和操作
也可以在原有类的基础上增添新的属性和操作
从一个类派生出另一个类时,原始类称为基类,继承类称为派生类
派生类继承基类所有的成员变量和函数
派生类可扩展自己的功能
二、继承中的访问权限
public > protected > private
派生类
public : 公有继承
基类 public 成员:在派生类中还是 public 属性,在派生类的内部和外部都可以访问
基类 protected 成员:在派生类中还是 protected 属性,在派生类的内部可以访问, 外部不可以访问
基类 private 成员:在派生类中还是 private 属性,在派生类的内部
protected: 保护继承
基类 public 成员:在派生类中还是 protected 属性,在派生类的内部可以访问, 外部不可以访问
基类 protected 成员:在派生类中还是 protected 属性,在派生类的内部可以访问, 外部不可以访问
基类 private 成员:在派生类中还是 private 属性,在派生类的内部和外部都不可以访问
private: 私有继承
基类 public 成员:在派生类中还是 private 属性,在派生类的内部可以访问, 外部不可以访问
基类 protected 成员:在派生类中还是 private 属性,在派生类的内部可以访问, 外部不可以访问
基类 private 成员:在派生类中还是 private 属性,在派生类的内部和外部都不可以访问
三、类型兼容性原则
1、子类对象可以被当作父类对象使用
void func1()
{
// Parent p;
Child p;
p.setAB(1,2);
p.printAB();
}
2、父类指针可以直接指向子类对象
void func2()
{
// c1 是子类对象的指针
Child *c1 = new Child;
c1->setAB(1,2);
c1->setC(3);
// p 是基类的指针 c1 是派生类的指针
// p 是父类的指针 c1 是子类的指针,指向子类的对象
// 父类指针可以直接指向子类对象, p可以直接指向一个子类的对象
// 所以可以直接将 c1 赋值给 p
// 将子类对象当作父类对象来使用
Parent *p;
p = c1;
// p 是 Parent 类型的指针,所以只能使用 Parent 中的内容
p->printAB();
Parent *p2 = new Child;
}
3、子类对象可以直接初始化父类对象
4、子类对象可以对父类对象赋值
5、父类引用可以直接引用子类对象
void func4()
{
Child c;
c.setAB(1,2);
c.setC(3);
Parent &p1 = c; //父类引用可以直接引用子类对象
// 子类对象可以直接初始化父类对象
Parent p = c; // Parent(c) ===> Parent(const Parent &obj) ====> Parent(&c) ==> Parent(const Parent *const obj)
test(&p);
// 子类对象可以对父类对象赋值
Child c2;
c2.setAB(5,6);
c2.setC(10);
// Parent &operator=(const Parent &obj)
// 父类引用可以直接引用子类对象
// p.operator=(c2);
p = c2;
test(p);
}
四、继承中的构造和析构
派生类的组成:基类成员 + 派生类自己的成员
原则:谁的成员谁自己去初始化,分工明确,
基 类:基类自己初始化(调用基类的构造函数) -----> 在对象初始化列表中显示调用基类的构造函数
派生类:派生类自己初始化(调用派生类的构造函数)
构造:先构造基类、再构造组合对象、再构造派生类自己
析构:与构造顺序相反
class Child :public Parent
{
public:
Child() : Parent(1,2)
{
c = 0;
cout << "Child 构造函数被调用" << endl;
}
~Child()
{
cout << "Child 析构函数被调用" << endl;
}
void printC()
{
printf ("c = %d\n", c);
}
private:
int c;
};
class Child :public Parent
{
public:
Child() : Parent(1,2), o1(1,2), o2(3,4)
{
c = 0;
cout << "Child 构造函数被调用" << endl;
}
~Child()
{
cout << "Child 析构函数被调用" << endl;
}
void printC()
{
printf ("c = %d\n", c);
}
private:
int c;
Object o1;
Object o2;
};
五、继承中的同名成员和同名函数
1、当基类成员变量和派生类成员变量重名的时候,默认使用的是派生类的成员变量
2、使用基类的同名成员变量,要使用域解析符指明所属的类
关于继承中的重载问题:
重载只能发生在一个类之中,派生类不能重载基类的同名函数
当派生类重载基类的函数的时候,会将基类的所有的同名函数屏蔽掉,不能再使用
重定义:派生类有和基类 成员函数原型一样的函数,叫做函数重定义
class Parent
{
public:
void print()
{
printf ("a = %d, b = %d\n", a, b);
}
void func()
{
printf ("基类: 无参\n");
}
void func(int a)
{
printf ("基类: 有一个参数\n");
}
public:
int a;
int b;
};
class Child : public Parent
{
public:
// 派生类的同名成员函数会屏蔽基类的同名成员函数
// 函数原型一样, 叫函数重定义
void print()
{
printf ("a = %d, c = %d\n", a, c);
}
void func(int a, int b)
{
printf ("派生类: 有两个参数\n");
}
public:
int a;
int c;
};
六、派生类中关键字 static 的使用
类的静态变量是所有派生类所共享的
静态变量存放在数据区,测算类的大小时,不包括静态变量
七、多继承
多重继承的语法:class 新类:基类列表(逗号分隔开)
多个父类的构造顺序和在继承时父类的声明顺序有关
基类指针指向派生类对象的时候,指针会根据基类数据成员在派生类中的
存储位置不同,做不同的偏移
#include <iostream>
using namespace std;
class Parent1
{
public:
Parent1 (int a, int b)
{
this->a = a;
this->b = b;
cout << "Parent1 构造函数被调用" << endl;
}
void print1()
{
printf ("a = %d, b = %d\n", a, b);
}
private:
int a;
int b;
};
class Parent2
{
public:
Parent2 (int a, int b)
{
this->c = a;
this->d = b;
cout << "Parent2 构造函数被调用" << endl;
}
void print2()
{
printf ("c = %d, d = %d\n", c, d);
}
private:
int c;
int d;
};
// 多重继承的语法:class 新类:基类列表(逗号分隔开)
// 多个父类的构造顺序和在继承时父类的声明顺序有关
class C:public Parent1, public Parent2
{
public:
C(int e):Parent1(1,2), Parent2(3,4)
{
this->e = e;
cout << "C 构造函数被调用" << endl;
}
void printc()
{
printf ("e = %d\n", e);
}
private:
int e;
};
void func1(Parent1 &p)
{
p.print1();
}
void func2(Parent2 &p)
{
p.print2();
}
int main()
{
cout << "sizeof C : " << sizeof C << endl;
C c(10);
func1(c);
func2(c);
// 基类指针指向派生类对象的时候,指针会根据基类数据成员在派生类中的
// 存储位置不同,做不同的偏移
Parent1 *p1 = &c;
Parent2 *p2 = &c;
printf ("&C = %p\n", &c);
printf ("p1 = %p\n", p1);
printf ("p2 = %p\n", p2);
return 0;
}
八、多继承的二义性
#include <iostream>
using namespace std;
class Parent1
{
public:
Parent1 (int a, int b)
{
this->a = a;
this->b = b;
cout << "Parent1 构造函数被调用" << endl;
}
void print1()
{
printf ("a = %d, b = %d\n", a, b);
}
public:
int a;
int b;
};
class Parent2
{
public:
Parent2 (int a, int b)
{
this->a = a;
this->d = b;
cout << "Parent2 构造函数被调用" << endl;
}
void print2()
{
printf ("c = %d, d = %d\n", a, d);
}
public:
int a;
int d;
};
class C:public Parent1, public Parent2
{
public:
C(int e):Parent1(1,2), Parent2(3,4)
{
this->e = e;
cout << "C 构造函数被调用" << endl;
}
void printc()
{
printf ("e = %d\n", e);
}
private:
int e;
};
int main()
{
C c(10);
//c.a = 18; 编译器不知道该调用哪个基类的 a
c.Parent1::a = 10;
c.Parent2::a = 12;
return 0;
}
#include <iostream>
using namespace std;
class Parent
{
public:
Parent (int a)
{
this->a = a;
cout << "Parent 构造函数被调用" << endl;
}
public:
int a;
};
class Parent1 :public Parent
{
public:
Parent1 (int b):Parent(1)
{
this->b = b;
cout << "Parent1 构造函数被调用" << endl;
}
void print1()
{
printf ("b = %d\n", b);
cout << "Parent1 构造函数被调用" << endl;
}
public:
int b;
};
class Parent2 :public Parent
{
public:
Parent2 (int b) :Parent(3)
{
this->d = b;
cout << "Parent2 构造函数被调用" << endl;
}
void print2()
{
printf ("d = %d\n", d);
}
public:
int d;
};
class C:public Parent1, public Parent2
{
public:
C(int e):Parent1(2), Parent2(4)
{
this->e = e;
cout << "C 构造函数被调用" << endl;
}
void printc()
{
printf ("e = %d\n", e);
}
private:
int e;
};
int main()
{
C c(10);
c.Parent1::a = 10;
return 0;
}
九、虚继承
如果一个派生类从多个基类产生,而这些基类又有一个共同的基类
,则在对该基类中声明的名字进行访问时,可能产生二义性,要使
这个公共基类在派生类中只产生一个子对象,必须对这个基类声明
为虚继承,使这个基类成为虚基类。
虚继承:解决多继承带来的二义性问题
#include <iostream>
using namespace std;
// 虚继承:解决多继承带来的二义性的问题
// 公共基类
class Parent
{
public:
Parent (int a = 0)
{
this->a = a;
cout << "Parent 构造函数被调用" << endl;
}
public:
int a;
};
// 虚继承自 公共基类
class Parent1 :virtual public Parent
{
public:
Parent1 (int b):Parent(1)
{
this->b = b;
cout << "Parent1 构造函数被调用" << endl;
}
void print1()
{
printf ("b = %d\n", b);
cout << "Parent1 构造函数被调用" << endl;
}
public:
int b;
};
class Parent2 :virtual public Parent
{
public:
Parent2 (int b) :Parent(3)
{
this->d = b;
cout << "Parent2 构造函数被调用" << endl;
}
void print2()
{
printf ("d = %d\n", d);
}
public:
int d;
};
// 派生类
class C:public Parent1, public Parent2
{
public:
C(int e):Parent1(2), Parent2(4)
{
this->e = e;
cout << "C 构造函数被调用" << endl;
}
void printc()
{
printf ("e = %d\n", e);
}
public:
int e;
};
int main()
{
C c(1);
Parent1 p1(1);
cout << "sizeof p1 = " << sizeof p1 << endl;
cout << "sizeof p2 = " << sizeof Parent2 << endl;
cout << "sizeof c = " << sizeof C << endl;
c.a = 1;
c.b = 2;
c.d = 3;
c.e = 4;
return 0;
}
虚继承会有虚基类指针 vbptr->b->vbptr->d->e->a;