在继承(一):https://blog.youkuaiyun.com/ly_6699/article/details/88801492 这篇博客里,我讲到了继承的概念和定义,基类和派生类对象赋值转换等相关的知识点,有兴趣的可以点链接去看。
【本节内容】
继承中的作用域
派生类的默认成员函数
3.继承中的作用域
3.1 在继承体系中,基类和派生类都有独立的作用域。
3.2 子类和父类有同名成员(成员函数或成员变量)时,子类成员将屏蔽对父类同名成员的直接访问,这种情况叫隐藏或重定义
所以在子类中要访问父类成员可以显式访问,即 基类::基类成员
3.3 父类对象无法调用子类成员,子类对象可以调用父类成员
3.4 不同作用域可以定义同名成员,但在实际应用的继承体系里最好不要定义同名成员,因为不指定成员的作用域时,都会优先访问子类成员
例1: 在下面的代码中,Student的成员变量 _num与Person的_num构成了隐藏(重定义)关系,虽然代码编译没有错误,但是非常容易混淆
class Person
{
protected:
string _name = "小明";
int _num = 111;
};
class Student :public Person
{
public:
void Print()
{
cout << "姓名: "<<_name << endl; //小明
cout << "身份证号:" << Person::_num <<endl; //111
cout << "身份证号:"<<_num <<endl; //未指定作用域 99
cout <<"学号:"<< _num << endl; //99
}
protected:
int _num = 99;
};
void Test()
{
Student s;
s.Print();
}
例2: 在下面代码中,B中的fun 函数 与A中的fun函数同名构成隐藏。
成员函数满足函数同名就构成隐藏。
注意:这里不是函数重载,因为不在同一作用域。
class A
{
public:
void fun() { cout << "A" << " "; }
};
class B :public A
{
public:
void fun(int i)
{
A::fun();
//fun(); //未指定作用域,编译不通过!!
cout << "B->" << i << endl;
}
};
void Test()
{
B b;
b.fun(10); //子类对象调用子类成员
b.A::fun(); //子类对象调用同名父类成员,已指定作用域
//b.fun(); //1.父类fun函数被隐藏 2.子类fun函数不匹配 所以编译不通过!!
A a;
a.fun(); //父类对象调用父类成员
//a.fun(10); //父类对象无法调用子类成员,编译不通过!!
}
4.派生类的默认成员函数
大家都知道,当我们定义类不写成员函数时,编译器会给类自动生成6个默认的成员函数,那么派生类的成员函数该怎样生成呢?
1.子类的构造函数:必须调用父类的构造函数去完成子类中父类那部分成员的初始化。如果父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显示调用。
2. 子类的拷贝构造函数: 必须调用父类的拷贝构造函数去完成子类中父类那部分成员的拷贝初始化。
3. 子类的赋值重载: 必须调用父类的operator=去完成子类中父类那部分成员的复制。
4. 子类的析构函数: 无需调用父类,系统会自动调用父类的析构函数完成自己空间的清理。
5. 派生类中函数的调用顺序
class Person //父类
{
public:
Person(const char* name = "ly") //构造,最好写成全缺省,方便子类直接调用
:_name(name)
{
cout << "Person()"<< 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, int num) //构造
:Person(name) //子类不能自己初始化父类成员,即不能用_name("ly")初始化
, _num(num)
{
cout << "Student(const char* name, int num)" << endl;
}
Student(const Student&s) //拷贝构造
: Person(s) //切片完成拷贝构造
, _num(s._num)
{
cout << "Student(const Student&s) " << endl;
}
Student &operator=(const Student &s) //赋值重载
{
cout << "Student &operator=(const Student &s)" << endl;
if (this != &s)
{
Person::operator=(s); //父类operator=和子类函数会构成隐藏,所以必须指明作用域
_num = s._num;
}
return *this;
}
~Student() //析构
{
cout << "~Student() " << endl; //父类空间编译器负责清理
}
protected:
int _num;
};
void Test()
{
Student s1("jack", 18);
Student s2(s1);
Student s3("rose", 17);
s3 = s1;
}
这里给大家一道的面试题:实现一个不能被继承的类。
解题思路有两个:
1.在C++98 里,我们可以将构造函数私有化,这样派生类无法调用基类的构造函数,便无法继承。
2. 在C++11 里,我们可以用关键字final,禁止类被继承
具体代码我会在下篇博客里给出,大家记得关注哦~
实现一个不能被继承的类:https://blog.youkuaiyun.com/ly_6699/article/details/88805277
另外:
继承(三):https://blog.youkuaiyun.com/ly_6699/article/details/88858186
讲到继承和友元,继承和静态成员,继承和组合
继承(四):https://blog.youkuaiyun.com/ly_6699/article/details/88858618
讲到复杂的菱形继承和菱形的虚拟继承