一、继承的概念
1、继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保存原有的特性基础上进行扩展,增加功能。
2、一个B类继承于A类,或称从类A派生类B。这样类A成为基类(父类),类B成为派生类(子类)。
派生类中的成员,包含两大部分:
· 一类是从基类继承过来的,一类是自己增加的成员
· 从基类继承过来的表现其共性,而新增的成员体现了其个性
二、派生类定义
1、语法
Class 派生类名 : 继承方式 基类名 { //派生类新增的数据成员和成员函数 }; 三种继承方式: public: 公有继承 private: 私有继承 protected: 保护继承
2、例如
class Person //定义一个Person基类
{
public:
void Print()
{
cout<<"name: "<<_name<<endl;
cout<<"age: "<<_age<<endl;
}
void set_name(string name)
{
_name = name;
}
string get_name()
{
return _name;
}
void set_age(int age)
{
_age = age;
}
int get_age()
{
return _age;
}
protected:
string _name; //名字
int _age; //年龄
};
//Student 派生类
class Student : public Person
{
private:
string _stuID;
};
//Teacher 派生类
class Teacher : public Person
{
private:
string _jobID;
};
int main() {
Teacher t;
Student s;
t.set_age(10);
t.set_name("zhanshan");
s.set_age(30);
s.set_name("Tony");
t.Print();
s.Print();
cout<<sizeof(Person)<<endl;
cout << sizeof(t) <<endl;
cout << sizeof(s) <<endl;
return 0;
}
输出可以看出:派生类与基类所占大小多了新增的数据成员。
三、派生类的访问控制
1、C++中的继承方式会影响子类的对外访问属性
public: 父类成员在子类中保持原有的访问级别 private: 父类成员在子类中变为private成员 protected: 父类中public成员会变成protected,父类中protected成员仍然为protected,父类中private仍然为private
父类成员访问控制
继承方式
public protected private publicpublic protected private protectedprotected protected private privateprivate private private
2、private成员在子类中依然存在,但是却无法访问到。不论何种方式继承基类,派生类都不能直接使用基类的私有成员。
private 和 protected 的区别:
private
成员是完全私有的,只有类本身可以访问,用于实现类的内部逻辑和数据隐藏。protected
成员在类内部和派生类中可见,用于在继承体系中共享代码和实现多态。
四、单继承中构造函数和析构函数的调用顺序
·子类对象在创建时会首先调用父类的构造函数
·父类构造函数执行完成后,才会调用子类的构造函数
·当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数
·析构函数调用顺序和构造函数相反
class Person //定义一个Person基类 { public: Person() {cout<<"Person()"<<endl;} ~Person() {cout<<"~Person()"<<endl;} } //Student 派生类 class Student : public Person { public: Student() {cout<<"Student()"<<endl;} ~Student() {cout<<"~Student()"<<endl;} } int main() { Student s; return 0; }输出:
五、派生类中调用基类的构造函数
方法一:基类中设计函数访问所有变量,派生类在构造函数内初始化成员变量
class Person //定义一个Person基类
{
public:
Person()
{cout<<"Person()"<<endl;}
~Person()
{cout<<"~Person()"<<endl;}
//用于访问私有变量
void set_name(const char* name)
{_name = name;}
string get_name()
{return _name;}
void set_age(int age)
{_age = age;}
int get_age()
{return _age;}
protected:
int _age; //年龄
private:
string _name; //名字
};
//Student 派生类
class Student : public Person
{
public:
Student()
{cout<<"Student()"<<endl;}
Student(int age,const char *name)
{
_age = age;
set_name(name);//调用函数初始化私有变量
cout<<"Student(int age,char *name)"<<endl;
}
~Student()
{cout<<"~Student()"<<endl;}
private:
string _stuID;
};
int main() {
Student b(10,"lisi");
cout<<b.get_age()<<endl;
cout<<b.get_name()<<endl;
return 0;
}
输出:
方法二: 基类中的构造函数使用初始化成员列表初始化成员变量
(在派生类中可以指定调用基类中的构造函数)
class Person //定义一个Person基类
{
public:
Person()
{cout<<"Person()"<<endl;}
~Person()
{cout<<"~Person()"<<endl;}
//基类构造函数使用初始化成员列表
Person(int age,const char* name):_age(age),_name(name)
{
cout<<"Person(int age,string name):_age(age),_name(name)"<<endl;
}
//用于访问私有变量
void set_name(const char* name)
{
_name = name;
}
string get_name()
{
return _name;
}
void set_age(int age)
{
_age = age;
}
int get_age()
{
return _age;
}
protected:
int _age; //年龄
private:
string _name; //名字
};
//Student 派生类
class Student : public Person
{
public:
Student()
{cout<<"Student()"<<endl;}
//在派生类中可以指定调用基类中的构造函数
Student(int age,const char *name): Person(age,name)
{
cout<<"Student(int age,const char *name): Person(age,name)"<<endl;
}
~Student()
{cout<<"~Student()"<<endl;}
};
int main() {
Student b(10,"lisi");
cout<<b.get_age()<<endl;
cout<<b.get_name()<<endl;
return 0;
}
输出:先调用基类带参数构造函数,在调用派生类带参数构造函数
Person(int age,string name):_age(age),_name(name) Student(int age,const char *name): Person(age,name) 10 lisi ~Student() ~Person()
六、派生类中的成员变量与基类中的成员变量名冲突(尽量避免这种情况发生)
//基类
class A
{
public:
A():a_x(10),a_y(20)
{}
int a_x;
int a_y;
};
//派生类
class B : public A
{
public:
B():a_x(20),b_y(20) //a_x一定是B类的
{
//可以在里面实现访问基类中的同名成员变量
A::a_x = 20;//将基类中的同名成员修改为20
}
int a_x;//和基类中的成员变量名字冲突
int b_y;
};
int main() {
//派生类会不会继承类中同名的属性呢?可以继承的!
cout<<sizeof(B)<<endl;//16 说明可以继承
cout<<sizeof(A)<<endl;//8
B b;
//b.a_x:派生类中的成员变量还是继承自基类的成员变量?
cout<<b.a_x<<endl;//20 说明是派生类的
//访问基类中的同名的成员变量
cout<<b.A::a_x<<endl;//20 基类名::基类中的成员变量
return 0;
}
总结:
1、当子类成员和父类成员同名时,子类依然从父类继承同名成员
2、如果子类有成员和父类同名,子类访问其成员默认访问子类的成员(本作用域,就近原则)
3、在子类通过作用域::进行同名成员区分(在派生类中使用基类的同名成员,显示使用类名限制符)
七、隐藏
1、子类函数与父类函数的名称相同,参数也相同,但是父类函数没有virtual,父类函数被隐藏
//基类
class A
{
public:
void func()
{
cout<<"A::func"<<endl;
}
};
//派生类
class B : public A
{
public:
//和基类中的成员函数同名,参数也相同 隐藏基类同名函数
void func()
{
cout<<"B::func"<<endl;
}
};
int main() {
B b;
b.func(); //B::func
b.A::func();//A::func
return 0;
}
2、子类的函数与父类的名称相同,但是参数不同,父类函数被隐藏
//基类
class A
{
public:
void func()
{
cout<<"A::func"<<endl;
}
};
//派生类
class B : public A
{
public:
//和基类中的成员函数同名,参数不相同
void func(int x)
{
cout<<"B::func(int x)"<<endl;
}
};
int main() {
B b;
b.func(10); //B::func(int x)
return 0;
}
八、多继承、环形继承、虚继承
1、C++中的多继承是指一个类可以同时继承多个基类的特性和行为
class Base1 { // Base1的成员 }; class Base2 { // Base2的成员 };class Base : public Base1, public Base2 { // Derived的成员 };Base类继承了Base1和Base2的公共和私有成员(但请注意,私有成员在派生类中仍然保持私有)。这意味着Base类的实例将能够访问Base1和Base2中的公共成员和Base自己定义的成员。
2、C++中的环形继承是指在类的继承关系中形成了一个循环,即类A继承自类B,类B又继承自类A或类B继承自类C,类C又继承自类A,这样就形成了一个闭环。例如:
//间接基类
class A
{
public:
int a;
};
//直接基类
class B : public A
{
public:
//int a;
int b;
};
//直接基类
class C : public A
{
public:
//int a;
int c;
};
//派生类
class D: public B,public C
{
//来自B
//int a;
//int b;
//来自C
//int a;
//int c;
int d;
};
int main() {
D d;
d.a = 10; //error: request for member 'a' is ambiguous
cout<<sizeof(D)<<endl; //8+8+4=20
return 0;
}
出现问题,D类初始化来自A类的成员变量是报错,编译器无法确定成员的访问路径。
3、C++中的虚继承是一种解决多重继承中菱形继承问题的方法
虚继承通过在继承列表中使用关键字virtual
来指定某个基类是以虚继承的方式被继承。这样,所有派生自同一个虚基类的类将共享一个基类对象的实例,避免了上述环形继承出现的重复和歧义问题。
//虚基类 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;};
九、静态成员的继承
静态成员的继承行为与非静态成员有所不同。当一个派生类继承自一个包含静态成员的基类时,基类中的静态成员不会创建新的副本,而是被派生类共享。这意味着派生类中访问静态成员时,实际上是访问的基类中的静态成员。
class A { public: static int static_member; // 静态成员声明 static int func(); }; int A::static_member = 100; // 静态成员定义 class B : public A { public: }; int main() { B b; b.static_member = 200; cout<<A::static_member<<endl; //200 说明静态成员变量是可以进行继承的 cout<<B::static_member<<endl; //200 return 0; }