C++中的继承详解
继承、派生类、基类
1.概念说明
继承:从先辈处得到
继承:从先辈处得到属性和行为特征,类的继承就是新的类从已有的类那里得到已有的特性。
基类:已有的类称为基类,也称为父类。
派生类:新的类称为派生类,也成为子类。
派生类同样也可以作为基类派生出新的类。
1.1类的继承方式的分类
1.公有继承(public)
2.保护继承(protected)
3.私有继承(private)
如果不显式给出继承方式关键字,则默认为私有继承(private)
2.派生类与基类的声明与构成
2.1声明
class Person//基类(父类)
{
private:
string Name;
public:
Person(string name) : Name(name)
{
}
void Print()
{
cout << "Name:" << Name << endl;
}
};
//派生类(子类)//公有继承
class Studet :public Person
{
private:
string sex; //新增加的数据成员
public:
Studet(string ssex,string name):sex(ssex),Person(name)
{
}
void Show() //新增加的成员函数
{
cout << "sex:" << sex << endl;
}
};
从上面代码中,我们分析得到"class Studet"之后,跟着":public Person",这意味着类Student继承了类Person。
其中类Person是基类,类Student是派生类。
关键字public指出基类Person中的成员在派生类Student中的继承方式(公有继承)。
由此可见,设计一个派生类的一般格式为:
class 派生类名称:[继承方式] 基类名称
{
派生类的数据成员和成员函数
};
"基类名称"是一个已经声明的类的名称
"派生类名称"是基础基类的特性而生成的新类的名称。
"继承方式"是前面所说的三种继承方式之一。
2.2构成
基类Person和派生类Student的关系
3.派生类与基类的构造与析构过程
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x)
{
cout << "Create Object: " << this << endl;
}
~Object()
{
cout << "Destory Object: " << this << endl;
}
};
class Test :public Object//先构建父对象,再构建子对象;先析构子对象,再析构父对象
{
public:
typedef Object _Base;
private:
int num;
public:
Test(int x = 0) :num(x), Object(x + 10)//_Base(x)
{
cout << "Create Test: " << this << endl;
}
~Test()
{
cout << "Destory Test: " << this << endl;
}
};
int main()
{
Test t1(10);
return 0;
}
从上述代码,我们可以得到:
(1)在构造过程中,先构建父对象,再构建子对象;
(2)在析构过程中,先析构子对象,再析构父对象
下面我们看一下这两个过程
4.派生类与基类所占字节数
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x)
{
cout << "Create Object: " << this << endl;
}
~Object()
{
cout << "Destory Object: " << this << endl;
}
};
class Test :public Object
{
public:
typedef Object _Base;
private:
int num;
public:
Test(int x = 0) :num(x), Object(x + 10)//_Base(x)
{
cout << "Create Test: " << this << endl;
}
~Test()
{
cout << "Destory Test: " << this << endl;
}
};
int main()
{
Object obj(10);
Test t1(10);
cout << "sizeof(Obj):" << sizeof(obj) << endl;
cout << "sizeof(t1):" << sizeof(t1) << endl;
return 0;
}
Obj和t1的具体构造
派生类对基类成员的访问规则
值得注意的是:可访问性:指的是对象的可访问性
1.三种继承的访问规则
不管是那种继承,我们都可以访问基类的public和protected成员,而private成员不可访问
class Object
{
private: int oa;
protected: int ob;
public: int oc;
};
class BasePublic :public Object
{
public:
void set()
{
//可以访问继承里面的公有和保护,不能访问继承里面的私有
Object::ob = 10;
Object::oc = 20;
}
};
class BaseProtected :protected Object
{
public:
void set()
{
//可以访问继承里面的公有和保护,不能访问继承里面的私有
Object::ob = 200;
Object::oc = 300;
}
};
class BasePrivate :private Object
{
public:
void set()
{
//可以访问继承里面的公有和保护,不能访问继承里面的私有
Object::ob = 20;
Object::oc = 30;
}
};
2.成员对象和继承来的对象的访问区别
不管是那种继承:
都可以访问成员对象里面的公有,不能访问成员对象里面的保护和私有
都可以访问继承里面的公有和保护,不能访问继承里面的私有
class Object
{
private: int oa;
protected: int ob;
public: int oc;
};
class BasePublic :public Object
{
public:
Object obj;//对象
public:
void set()
{
//可以访问成员对象里面的公有,不能访问成员对象里面的保护和私有
//obj.oa = 10;//error
//obj.ob = 10;//error
obj.oc = 10;
//可以访问继承里面的公有和保护,不能访问继承里面的私有
//Object::oa = 10;//error
Object::ob = 20;
Object::oc = 30;
}
};
派生类的应用
1.赋值兼容规则和切片现象
赋值兼容规则只适合于公有派生
class Person
{
private:
int value;
public:
Person(int x = 0) :value(x) { }
};
class Student :public Person
{
private: int num;
public:
Student(int x = 0) :num(x), Person(x + 10) { }
};
void dance(const Person& s){ }
void Study(const Student& s){ }
int main()
{
Person se(23);
Student st(10);
Person& sp = st;//引用
Person* p = &st;
se = st;
dance(se);
dance(st);
Study(st);
//Study(se);//error
return 0;
}
下面,我们分析一下具体过程
执行Person se(23);
Student st(10);时:
执行Person& sp = st;//引用
Person* p = &st;
执行se = st;
当把子对象给父对象(前提条件是公有继承),发生了切片现象。
通过上面的代码分析我们可以得到:
(1)派生类对象可以向基类对象赋值;
(2)派生类对象可以初始化基类对象的引用
2. 同名隐藏
2.1数据成员的同名隐藏
下面以代码为例说明
class Object
{
public:
int value;
public:
Object(int x = 0) :value(x) { }
void Print()
{
cout << "Object:value: " << value << endl;
}
};
class Base :public Object
{
public:
int value;
public:
Base(int x = 0) :Object(x), value(x + 10) { }
void Set()
{
value = 100;//Base里面的value(就近原则),把父对象的value隐藏了
//Object::value = 10;//value是protected或者pubic
//同名隐藏
}
void Show()
{
cout << "Base:value: " << value << endl;
}
};
int main()
{
Object obj(1);
Base ba(2);
obj.Print();
ba.Show();
ba.Print();
ba.Set();
ba.Show();
ba.Print();
return 0;
}
2.2成员函数的同名隐藏
同样,我们以代码为例说明
子对象调动时会隐藏父对象的同名函数
class Object
{
public:
protected:
int value;
public:
Object(int x = 0) :value(x) { }
void Print(int x =0)
{
value = x;
cout <<"Object:value: " <<value << endl;
}
void Show()
{
cout << "Object:value: " << value << endl;
}
};
class Base :public Object
{
public:
int num;
public:
Base(int x = 0) :Object(x), num(x + 10) { }
void Print()const
{
cout <<"hello !"<< endl;
}
};
int main()
{
Base base1(10);
Object obj1(100);
base1.Print();//子对象的
base1.Object::Print();//父对象的方法
//base1.Print(10);//error
//子对象调动时会隐藏父对象的同名函数,不是重载的意义
base1.Object::Print(80);//OK
base1.Show();
obj1.Show();
return 0;
}