1、继承是什么
继承是代码的复用,被继承的类称为基类(父类),继承的新类叫派生类(子类)。
继承的类拥有父类的所有成员变量和成员函数,相当于类的完全复制。
2、继承的语法
class 新类名 :访问控制 要继承的类
例如:class new:public old------>new类继承了old类。
#include <iostream>
#include <string>
using namespace std;
//父类(基类)
class people
{
public:
void eat()
{
cout<<"吃饭"<<endl;
}
void slee()
{
cout<<"睡觉"<<endl;
}
public:
int id;
string name;
string sex;
};
//子类(派生类)
class student:public people
{
public:
void study()
{
cout<<"学习"<<endl;
}
};
class worker:public people
{
public:
void work()
{
cout<<"工作"<<endl;
}
};
int main1()
{
student s;
s.study();
worker w;
w.work();
return 0;
}
3、基类成员在派生类中的访问权限
min(基类中的权限,继承时的访问限制)
其中:public>protected>private
1、对于 public的访问权限,基类中对应访问权限在派生类的情况
public:类的内部和外部都能使用。
protected:在类内部可以使用,在外部不能使用。
private:在类的内部和外部都不能使用。
2、对于 protected的访问权限,基类中对应访问权限在派生类的情况
public:在类内部可以使用,在外部不能使用
protected:在类内部可以使用,在外部不能使用。
private:在类的内部和外部都不能使用。
3、对于 private的访问权限,基类中对应访问权限在派生类的情况
public:在类内部可以使用,在外部不能使用
protected:在类内部可以使用,在外部不能使用。
private:在类的内部和外部都不能使用。
对于protected与private访问权限的区别:
在二次或多次继承后,protected的访问权限能在类内部使用,private的访问权限不能在类内部使用。
class Parent
{
public:
Parent(int var = -1)
{
}
public:
int m_nPub;
protected:
int m_nPtd;
private:
int m_nPrt;
};
class Child1:public Parent
{
public:
int getPub() {return m_nPub;}
int getPtd() {return m_nPtd;}
int getPrt() {return m_nPrt;} // A 错
};
class Child2:protected Parent
{
public:
int getPub() {return m_nPub;}
int getPtd() {return m_nPtd;}
int getPrt() {return m_nPrt;} // B 错
};
class Child3:private Parent
{
public:
int getPub() {return m_nPub;}
int getPtd() {return m_nPtd;}
int getPrt() {return m_nPrt;} // C 错
};
int main()
{
Child1 cd1;
Child2 cd2;
Child3 cd3;
int nVar = 0;
cd1.m_nPub = nVar; // D 对
cd1.m_nPtd = nVar; // E 错
nVar = cd1.getPtd(); // F 对
cd2.m_nPub = nVar; // G 错
nVar = cd2.getPtd(); // H 对
cd3.m_nPub = nVar; // I 错
nVar = cd3.getPtd(); // J 对
return 0;
}
4、对象模型(类型的兼容性原则)
1、一般情况下派生类对象可以替换基类对象
2、基类指针可以操作派生类对象(指针能做的事情与指向对象无关,之于本身的类型有关)
3、因为引用的本质是指针,因此基类引用可以引用派生类对象。
4、可以用派生类对象对基类对象构造(内部调用拷贝构造)和赋值(调用=运算符重载)
// 1、父类对象存在的地方都能用派生类对象进行替换
int main()
{
parent p;
p.getp(1,2);
child c;
c.getp(2,3);
c.showp();
return 0;
}
// 2、基类指针可以操作派生类对象
int main2()
{
child c;
parent *pa = &c;
// 指针能做的事情基于指针本身的类型,和他指向的对象类型无关
pa->getp(3,4);
pa->showp();
// 3、可以用基类的引用引用派生类对象
parent &p3 = c; // Parent *const p3 = &c1
p3.showp();
return 0;
return 0;
}
// 4、可以用派生类对象对基类对象进行构造、赋值
int main()
{
child c1;
c1.getp(1,2);
c1.getc(3,4);
// Parent(const Parent &obj)
parent p1 = c1; // ===> p1的拷贝 构造函数
p1.showp();
// operator =(const Parent &obj)
p1 = c1; // 赋值运算符重载函数
p1.showp();
return 0;
}
5、对象的构造
1、原则:谁的成员由谁对其负责初始化---->调用相应构造函数---->在初始化列表中
2、构造的顺序:
(1、先调用基类的构造函数,对基类成员进行构造。
(2、调用自身的构造函数,对其成员进行构造。
(3、析构的顺序与其相反。
#include <iostream>
using namespace std;
// 对象的构造: 谁的成员谁负责初始化 ---> 手动调用相应的构造函数 ---> 在对象初始化列表中
class Parent
{
public:
Parent()
{
cout << "无参构造" << endl;
}
Parent(int a, int b)
{
cout << "Parent(int a, int b) 构造函数" << endl;
m_a = a;
m_b = b;
}
protected:
int m_a;
int m_b;
};
class Test
{
public:
Test(int a, int b)
{
cout << "test 构造函数" << endl;
}
private:
int m_a;
int m_b;
};
// 继承中的构造顺序:1、先调用基类的构造函数对基类成员进行初始化 2、再调用自己的构造函数对象自己的成员进行初始化---->类似前置递归
// 继承中的析构顺序:和构造顺序相反---->类似后置递归
class Child : public Parent
{
public:
Child(int a, int b, int c, int d): t1(c, d), Parent(a, b), m_c(c), m_d(d)
{
cout << "Chil 构造函数" << endl;
//m_a = a;
//m_b = b;
m_c = c;
m_d = d;
}
protected:
int m_c;
int m_d;
Test t1;
};
int main()
{
Parent p1(1,2);
Child c(1,2,3,4);
return 0;
}
class A
{
public:
A()
{
cout << "A 的构造*****************" << endl;
}
~A()
{
cout << "A 的析构-----------------" << endl;
}
};
class B : public A
{
public:
B()
{
cout << "B 的构造*****************" << endl;
}
~B()
{
cout << "B 的析构-----------------" << endl;
}
};
class C : public B
{
public:
C()
{
cout << "C的构造*****************" << endl;
}
~C()
{
cout << "C 的析构-----------------" << endl;
}
};
class D : public C
{
public:
D()
{
cout << "D 的构造*****************" << endl;
}
~D()
{
cout << "D 的析构-----------------" << endl;
}
};
6、基类与派生类中同名成员的处理方式
1、如果基类与派生类中有同名成员,满足就近原则,默认使用的是派生类中的成员
2、基类同名成员的使用:派生类对象 基类类名::同名成员变量。
3、基类与派生类不能同时重载一个函数,函数重载只能发生在一个类中,如果派生类重载了基类中的函数,会将基类中的重载函数全部屏蔽。尽量不要在派生类中重载基类的函数。
4、函数重定义:派生类中的函数原型与基类中的函数原型一致。
5、对于基类中的静态成员变量,所有派生类对象都可以使用。