继承是面向对象复用的重要手段,通过继承定义一个类继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西。
在前面我们学到类和对象的时候有三种访问限定符,1.public(公有)2.protected(保护)3.private(私有),当时学的时候我们知道,在类外protected和private不能被访问,可是那为什么,还要定义一个protected呢?告诉你吧,他们在继承中体现了不一样。
一 在继承中有三种继承方式:
二 三种继承关系下基类成员在派生类的访问关系变化图:
总结:1.基类的私有成员在派生类是不可见的(不能被访问),如果一些基类成员不想被基类对象直接访问,但需要在派生类中访问,就定义为保护成员。可以看出保护成员限定符是因为继承才出现的。
2.public继承是一个接口继承,保持is_a原则,每个父类可用的成员对子类也可用,每个子类是一个父类,因此每个子类对象也都是一个父类对象(前提是父类没有定义私有成员)
3.protected/privated继承是一个实现继承 ,是has-a原则,父类的东西有可能在类外不可用,或不能访问,一个基类对象有一个派生类对象,父类和子类访问权限不一。
4.不管哪种继承方式,在派生类中都可以访问基类的公有和保护成员,但是基类的私有成员在子类都存在,不可见即不能访问
5.使用关键字class的默认继承方式是private,使用关键字struct 默认的继承方式是public
思考:保护继承,A父类成员在B子类全部变成保护的(除父类私有成员),则在类外都不能访问
私有继承,A父类成员在B子类全部都变成私有的(除父类私有),则在类外也都不能访问
那这两种继承有什么区别呢?区别在于下一个继承子类C中,保护继承B中的成员可见,而私有继承中B中
成员全部变为不可见。
三 通过继承父类的所有成员变为子类的一部分(包括成员变量,成员函数)算对象大小时不算成员函数,因为成 员函数编译是一段指令,放代码段,所有对象共享.
#include<iostream>
using namespace std;
class A
{
public:
int _a;
public:
void FA()
{
cout << "void FA()" << endl;
}
};
class B :public A
{
public:
void FB()
{
cout << "void FB()" << endl;
}
public:
int _b;
};
int main()
{
B b;
A a;
a._a = 1;
b._a = 2;//说明成员变量可以被继承
b._b = 3;
b.FA();//说明成员函数也可以被继承
return 0;
}
四 赋值兼容规则
1.子类对象可以赋值给父类对象(切片)
2.父类对象不能赋值给子类对象
3.父类的指针/引用可以指向子类对象(切片)
4.子类的指针和引用不能指向父类对象(可以通过强制类型转换完成)
#include<iostream>
using namespace std;
class A
{
public:
int _a;
public:
void FA()
{
cout << "void FA()" << endl;
}
};
class B :public A
{
public:
void FB()
{
cout << "void FB()" << endl;
}
public:
int _b;
};
int main()
{
B b;
A a;
a._a = 1;
b._a = 2;//说明成员变量可以被继承
a = b; //(切片)
//b = a;//不可以
A* p1 = &b;//切片 虽然指向b,但是只访问A类型那么大的空间
A& r1 = b;//
//B* p2 = &a;编译不通过
//B& r2 = a;编译不通过
B* p2 = (B*)&a; //可以编译通过
B& r2 =(B&) a;//可以编译通过
cout << p2->_b << endl;//越界读不出错
p2->_b = 10;//越界写出错
return 0;
}
五 继承体系中的作用域
1.在继承体系中基类和派生类都有自己独立的作用域
2.子类和父类中有同名成员,子类对象访问将隐藏子类从父类继承下来的同名成员(包括成员指针,成员函数)
面试爱考
#include<iostream>
using namespace std;
class A
{
public:
int _a;
public:
void FA()
{
cout << "void FA()" << endl;
}
};
class B :public A
{
public:
void FB()
{
cout << "void FB()" << endl;
}
public:
int _b;
};
int main()
{
B b;
A a;
b._a = 1;//访问子类的,隐藏
return 0;
}
面试爱考:
#include<iostream>
using namespace std;
class A
{
public:
int _a;
public:
void F(int a)
{
cout << "void FA()" << endl;
}
};
class B :public A
{
public:
void F(char* a)
{
cout << "void FB()" << endl;
}
public:
int _b;
};
int main()
{
B b;
b.F(10);//子类F(char*a)隐藏父类F(int a),但是又传的是10,与char* 不匹配,因此报错
return 0;
}
六 派生类的默认成员函数
在继承关系里面,在派生类中如果没有显示定义这六个成员函数,编译系统会默认合成这六个默认的成员函数
重点:
#include<iostream>
using namespace std;
class Person
{
public:
Person(const char*name = "nihao ")
:_name(name)
{
cout << "Person(const char*name)" << endl;
}
Person(const Person& p)
:_name(p._name)
{
cout << "Person(const Person& p)" << endl;
}
Person& operator=(const Person& p)
{
cout << "Person& operator=(const Person& p)" << endl;
if (this != &p)
{
_name = p._name;
}
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char*name = "haha", int d = 2)
:_d(d)
, Person(name)// _name(name)错误 子类只初始化自己的成员,显示调用父类的构造函数来初始化父类的
{
cout << "Student(const char*name, int d)" << endl;
}
Student(const Student&s)
:_d(s._d)
, Person(s)//切片子类对象赋给父类引用 子类只初始化自己的成员,显示调用父类的构造函数来初始化父类的
{
cout << "Student(const Student&s)" << endl;
}
Student& operator=(const Student&s)
{
cout << "Student& operator=(const Student&s)" << endl;
if (this != &s)
{
this->Person::operator=(s);//必须指定父类的operator=,不然出现栈溢出,因为子类隐藏了父类的同名函数
_d = s._d;
}
return *this;
}
~Student()
{
cout << " ~Student() " << endl;//子类析构完成时,会自动调用父类析构
}
protected:
int _d;
};
int main()
{
Student s("daidai", 12);
Student s2(s);
Student s3("hehe", 4);
s3 = s;
system("pause");
return 0;
}
七 单继承&多继承
菱形继承:存在:二义性 -------》指定作用域可以解决
数据冗余------》虚继承可以解决
菱形继承对象模型:
代码如下:
#include<iostream>
using namespace std;
class A
{
public:
int _a;
};
class B :public A
{
public:
int _b;
};
class C :public A
{
public:
int _c;
};
class D :public B, public C
{
public:
int _d;
};
int main()
{
D d;
cout << sizeof(d) << endl;//20
d._a = 1;//菱形继承存在二义性
d.B::_a = 1;//指定作用域解决了二义性
d.C::_a = 2;//指定作用域解决了二义性
d._c = 3;
d._b = 4;
d._d = 5;
system("pause");
return 0;
}
菱形虚拟继承对象模型
#include<iostream>
using namespace std;
class A
{
public:
int _a;
};
class B :virtual public A
{
public:
int _b;
};
class C :virtual public A
{
public:
int _c;
};
class D :public B, public C
{
public:
int _d;
};
int main()
{
D d;
cout << sizeof(d) << endl;//24
d.B::_a = 1;
d.C::_a = 2;
d._c = 3;
d._b = 4;
d._d = 5;
system("pause");
return 0;
}
看下面一个题,面试爱考
与
八 友元关系不能继承,也就是说基类友元,不能访问子类私有和保护成员
#include <iostream>
using namespace std;
class B;
class A
{
public:friend void Display(A& a, B& b);
A(int a = 1)
:_a(a)
{}
private:int _a;
};
class B :public A
{
public:B(int b = 1)
:_b(b)
{}
protected:
int _b;
};
void Display(A& a, B& b)
{
cout << a._a << endl;
cout << b._b << endl;//友元不能被继承
}
int main()
{
A a(2);
B b(3);
Display(a, b);
system("pause");
return 0;
}
九 继承与静态成员(可以继承)
基类定义了static成员,则整个继承体系里面只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例
#include <iostream>
using namespace std;
class Person
{
public: Person()
{
++ _count;
}
public:
static int _count;
};
int Person::_count = 0;
class Student :public Person
{
protected:int _stunum;
};
class Graduate :public Student
{
protected:int _id;
};
int main()
{
Person s1;
Student s2;
Graduate s3;
cout << "人数" << Person::_count << endl;
Student::_count = 0;
cout << "人数" << Person::_count << endl;
system("pause");
return 0;
}