1.概念
继承机制是面向对象程序设计时,提高代码复用率最重要的手段,有了它,程序员在保证原有特点的情况下,对该类进行拓展,增加功能,形成新的类。继承呈现了面向对象程序设计时的层次结构。
class base
{
public:
int _pub;
private:
int _pri;
protected:
int _pro;
};
class derived:public base //继承
{
};
int main()
{
cout << sizeof(base) << endl; //基类的大小
cout << sizeof(derived) << endl; //派生类的大小
return 0;
}
2.继承的格式
继承方式有三种:public/protected/private。
3.继承基类成员访问方式的变化
(1)基类private成员在派生类中怎么都是不可见的。不可见指的是:基类中的私有成员被继承到派生类当中,但是语法上限制它不管在类里边还是外边都不可以被访问
(2)基类的private成员在派生类中不可被访问,如果基类成员不想在类外被访问,但是想在派生类中被访问,就定义成protected。所以保护成员的限定符因为继承才出现的。
(3)基类中public成员,由继承方式的不同,在派生类中的表现方式也不同。(看下表)
(4)使用关键词class时默认的继承方式时private
4.基类和派生类对象之间的赋值转化
(1)子类对象可以赋值给父类的对象/引用/指针
(2)基类对象不能赋值给派生类的对象
(3)基类指针可以通过强制类型转化赋值给派生类的指针
class base
{
public:
int _pub;
};
class derived:public base
{
public:
int _pobD;
};
int main()
{
derived d;
d._pub = 20;
base b;
//1.子类对象可以赋值给父类的对象/引用/指针
b = d;
base *b1 = &d;
base &b2 = d;
cout << b._pub << endl;
cout << (*b1)._pub << endl;
cout << b2._pub << endl;
//2.基类对象不能赋值给派生类的对象
//d = b; //报错
//3.基类指针可以通过强制类型转化赋值给派生类的指针
derived *d1 = (derived*)b1;
cout << (*d1)._pub << endl;
system("pause");
return 0;
}
5.继承的作用域
(1)在继承体系中,基类和派生类都有自己的独立的作用域
(2)如果子类和父类中,有同名成员,子类将屏蔽父类对同名函数的直接访问,这种情况就叫隐藏,也叫重定义。(在子类的成员函数中,可以使用基类::基类成员显示访问)
(3)如果时成员函数的隐藏,只需要函数名相同就可以构成隐藏
(4)在实际中,继承体系最好不要出现同名的成员或者成员函数
class base
{
public:
int _pub;
void print()
{
cout << &_pub << endl;
}
};
class derived :public base
{
public:
int _pub;
public:
void print()
{
cout << &_pub << endl;
}
};
int main()
{
base b;
derived d;
b.print();
d.print();
system("pause");
return 0;
}
子类和父类中的成员是一样的,但是所对应的地址不一样,所以它们拥有不同的作用域。
class base
{
public:
int _pub=10; //同名成员
};
class derived :public base
{
public:
int _pub=20; //同名成员
public:
void print()
{
cout << base::_pub << endl; //基类::基类成员
cout << _pub << endl; //子类和父类有同名成员,父类的同名成员隐藏
}
};
int main()
{
base b;
derived d;
d.print(); //打印20
system("pause");
return 0;
}
class base
{
public:
int _pub=10;
void print() //成员函数名相同
{
cout << _pub << endl;
}
};
class derived :public base
{
public:
int _pub=20;
public:
void print(int i) //成员函数名相同
{
cout << i << endl;
cout << _pub << endl; //子类和父类有同名函数,父类的同名函数隐藏
}
};
int main()
{
base b;
derived d;
d.print(10); //打印10 20
system("pause");
return 0;
}
6.派生类的默认成员函数
(1)派生类的构造函数必须调用积累的构造函数来初始化基类的那一部份成员。如果基类没有默认的构造函数,则必须在派生类构造函数初始化列表的地方显式调用
(2)派生类的拷贝构造函数必须调用基类的拷贝构造函数完成基类的拷贝
(3)派生类的赋值运算符重载必须调用基类的赋值运算符重载完成对基类的复制
(4)派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类的成员。保证先清理派生类对象,再清理基类对象
(5)派生类对象初始化时,先调用基类的构造函数,再调用派生类的构造函数
(6)派生类对象清理时,先调用派生类的析构函数,再调用基类的析构函数
class base
{
public:
base(int b)
:_b(b)
{
cout << "基类构造函数" << endl;
}
base(const base &b)
:_b(b._b)
{
cout << "基类拷贝构造函数" << endl;
}
base& operator=(const base &b)
{
cout << "基类的赋值运算符重载" << endl;
if (this != &b)
_b = b._b;
return *this;
}
~base()
{
cout << "基类的析构函数" << endl;
}
public:
int _b;
};
class derived :public base
{
public:
derived(int b, int d)
:_d(d)
,base(b)
{
cout << "派生类构造函数" << endl;
}
derived(const derived &d)
:base(d)
,_d(d._d)
{
cout << "派生类拷贝构造函数" << endl;
}
derived& operator=(const derived &d)
{
cout << "派生类赋值运算符重载" << endl;
if (this != &d)
{
base::operator=(d);
_d = d._d;
}
return *this;
}
~derived()
{
cout << "派生类析构函数" << endl;
}
public:
int _d;
};
void test()
{
derived d(10, 20);
derived d1(d);
derived d2(30, 40);
d1 = d2;
//先调用派生类析构函数,再调用基类的析构函数
}
int main()
{
test();
system("pause");
return 0;
}
面试题:实现一个不能被继承的类
(1)构造函数私有化,派生类得不到基类的构造函数,所以不能别继承
class noinherit
{
public:
static noinherit get()
{
return noinherit();
}
private:
noinherit()
{}
};
(2)加final关键字
class noinherit final
{
};
7.继承与友元
友元函数不能被继承,也就是说基类友元函数不能访问派生类的保护和私有成员。
class derived;
class base
{
public:
friend void display(const derived &d,const base &b);
public:
int _b;
};
class derived :public base
{
protected:
int _d;
};
void display(const derived &d, const base &b)
{
cout << b._b << endl;
cout << d._d << endl;//报错 无法访问 protected 成员(在“derived”类中声明)
}
int main()
{
base b;
derived d;
display(d,b);
system("pause");
return 0;
}
8.继承与静态函数
基类定义的static成员,在整个继承体系中,只能有一个这样的成员。无论基类派生出多少的派生类,静态成员都只有一个。
class base
{
public:
base()
{
++_b;
}
public:
static int _b;
};
int base::_b = 0;
class derived :public base
{
public:
int _d;
};
class derived1 :public base
{
public:
int _d1;
};
int main()
{
cout << base::_b << endl; //0
derived d1;
cout << base::_b << endl; //1
derived d2;
cout << base::_b << endl; //2
derived d3;
cout << base::_b << endl; //3
derived1 d4;
cout << base::_b << endl; //4
system("pause");
return 0;
}
9.复杂的继承和菱形虚拟继承
单继承:一个类只有一个直接父类
多继承:一个类有多个直接父类
菱形继承:它是多继承特殊的一种
菱形继承的问题:derived3中有两份base成员。
class base
{
public:
int _b;
};
class derived1 :public base
{
public:
int _d1;
};
class derived2 :public base
{
public:
int _d2;
};
class derived3 :public derived1,public derived2
{
public:
int _d3;
};
int main()
{
derived3 d3;
//d3._b = 10; //_b的来源不明确(二义性)
d3.derived1::_b = 10;
d3.derived2::_b = 20;//这种_b的来源明确,但是_b重复出现(冗余)
system("pause");
return 0;
}
我们可以用虚拟继承来解决菱形继承的来源不明确(二义性)和重复出现(冗余)的问题。
class base
{
public:
int _b;
};
class derived1 :virtual public base
{
public:
int _d1;
};
class derived2 :virtual public base
{
public:
int _d2;
};
class derived3 :public derived1,public derived2
{
public:
int _d3;
};
int main()
{
derived3 d3;
d3.derived1::_b = 1;
d3.derived1::_b = 2;//006FFEE0
d3._d1 = 3;//006FFED0
d3._d2 = 4;//006FFED8
d3._d3 = 5;//006FFEDC
system("pause");
return 0;
}
虚拟继承在内存中的模型 :
原理如下图: