继承方式与权限问题
继承实质与权限问题
-
继承的实质: 父类的数据和成员子类中有一份
-
权限问题: 继承方式只会增强父类属性在子类中的权限显示
public | protected | private | |
public继承 | public | protected | private(不可直接访问) |
protected继承 | protected | protected | private(不可直接访问) |
private继承 | private | private | private(不可直接访问) |
继承的写法
- 继承: 子类中没有产生新的属性或者行为
- 派生: 派生类中有新的属性和行为产生
class 子类名:继承方式 父类名
{
//继承方式 就是权限限定词 public,protected,private
};
//父类 基类
class parent
{
};
//子类 派生类
//公有继承
class son1:public parent
{
public:
protected:
};
//保护继承
class son2:protected parent
{
public:
protected:
};
//私有继承
class son3:private parent
{
public:
protected:
};
下面我就用代码来写一下继承的具体写法与作用
class parent
{
public:
void print()
{
cout << name << "\t" << money << endl;
}
protected:
string name="默认";
int money = 10;
};
//子类
class son :public parent
{
public:
void printSon()
{
print();
cout << name << "\t" << money << endl;
}
};
int main() {
son boy;
boy.printSon();
/*
打印结果
默认 10
默认 10
*/
return 0;
}
由打印结果可以看出子类是可以调用父类的成员函数,也可以直接使用数据成员(私有成员除外,这就是保护属性与私有属性的区别)
class parent
{
public:
void print() {
cout << name << endl;
}
private:
string name = "父类";
};
这里我们先定义一个父类,在私有属性下定义一个name成员
可以看到我们子类中是没有办法使用这个数据成员的,但是也可以用一个函数去间接调用,这就是为什么我上面说的私有属性不可以直接访问
接下来我们再来看看三种不同的继承方式有什么区别
class A
{
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B :public A //B类用公有的继承方式继承A类
{
public:
//int a1;
protected:
//int a2;
private:
//int a3; 不能直接访问
//可以看到,共有继承是保持父类中数据的属性
};
class C :protected A //C类用保护的继承方式继承A类
{
public:
protected:
//int a1; //public 显示protected
//int a2; //保护继承会把除私有以外的数据属性变成保护属性
private:
//int a3; 不能直接访问
};
class D :private A //D类用私有的继承方式继承A类
{
public:
void print()
{
cout << a1 << endl;
cout << a2 << endl;
//cout << a3 << endl; //父类的私有属性不能直接访问
}
protected:
private:
//int a1; //私有继承直接把所有的数据都变成私有属性
//int a2;
//int a3; //父类的私有属性不能直接访问
};
私有继承还有一种说法就是阉割继承!因为一旦用私有继承了父类,那么父类的所有数据都变成私有属性,孙子类继承子类就无法访问父类的所有数据。这个也很好理解,当年被阉割了,还能再生孩子吗?
继承中的构造函数
-
父类的属性通过父类的构造函数初始化
-
子类中的构造函数,必须要调用父类构造函数,必须采用初始化参数列表的方式
-
单继承和多继承
-
单继承: 只有一个父类
-
多继承: 两个或者两个以上的父类
-
-
继承的属性:类可以被继承很多次,但是我们一般不会继承很多层,会导致类的臃肿,因为子类必须调用父类的构造函数,如果你继承很多层,那么构造函数会变得很复杂
class Parent
{
public:
Parent() { cout << "父类无参构造函数" << endl; }
protected:
string FName;
string SName;
};
class Son :public Parent
{
public:
//这种写法,父类必须存在无参的构造函数,当然缺省也可以
Son() { cout << "子类无参构造函数" << endl; }
protected:
string sonFName;
string sonSName;
};
int main() {
Son son;
/*
打印结果
父类无参构造函数
子类无参构造函数
*/
return 0;
}
由代码打印结果可知,我们在用子类构造对象的时候,一定是先调用父类的构造函数,再调用自己的构造函数
class Parent
{
public:
Parent(string FName, string SName) :FName(FName), SName(SName) {}
protected:
string FName;
string SName;
};
class Son :public Parent
{
public:
Son(string FName, string SName, string sonSName) :Parent(FName, SName)
{
//自己的属性用什么办法初始化都行
//最重要的应该是学会怎么在子类写构造函数!!
this->sonFName = FName;
this->sonSName = sonSName;
}
void print()
{
cout << "父:" << FName + SName << endl;
cout << "子:" << sonFName + sonSName << endl;
}
protected:
string sonFName;
string sonSName;
};
int main() {
Son son1("白", "老鬼", "日天");
son1.print();
/*
打印结果
父:白老鬼
子:白日天
*/
return 0;
}
上面我没有采用初始化参数列表来初始化子类的数据成员,那么下面我们在构造多继承的时候就使用初始化参数列表来初始化子类数据(子类数据什么初始化都可以!)
//多继承
//假设男生叫欧田
//假设女生叫阳子
//女儿就叫欧阳 田子
class MM
{
public:
MM(string mmFName, string mmSName):mmFName(mmFName),mmSName(mmSName){}
protected:
string mmFName;
string mmSName;
};
class GG
{
public:
GG(string ggFName, string ggSName):ggFName(ggFName), ggSName(ggSName){}
protected:
string ggFName;
string ggSName;
};
class Girl :public GG, public MM
{
public:
Girl(string mmFName, string mmSName, string ggFName, string ggSName)
:MM(mmFName, mmSName), GG(ggFName, ggSName),
girlFName(ggFName + mmFName), girlSName(ggSName + mmSName){}
void print()
{
cout << "父:" << ggFName + ggSName << endl;
cout << "母:" << mmFName + mmSName << endl;
cout << "女:" << girlFName + girlSName << endl;
}
protected:
string girlFName;
string girlSName;
};
int main() {
Girl girl("阳", "子", "欧", "田");
girl.print();
/*
打印结果
父:欧田
母:阳子
女:欧阳田子
*/
return 0;
}
继承中同名问题
-
数据成员同名
-
成员函数名同名
继承中的同名问题,我们要记住一个东西就是,除非做处理要不然都是就近原则!
做个点比较简单,我们直接上代码,不多废话了!!
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
void print()
{
cout << "MM:";
cout << name << "\t" << age << endl;
}
protected:
string name;
int age;
};
class Girl :public MM
{
public:
Girl(string name, int age) :MM("父类", 28), name(name), age(age) {}
void print()
{
//不做特别处理,就近原则
cout <<"Girl:" << name << "\t" << age << endl;
//类名限定
cout << MM::name << "\t" << MM::age << endl;
//不做特别处理,就近原则
MM::print();
}
protected:
string name;
int age;
};
int main() {
Girl girl("女孩",16);
girl.print();
/*
打印结果
Girl:女孩 16
父类 28
MM:父类 28
*/
return 0;
}
构造顺序问题
-
单继承中的构造顺序问题
-
先构造父类的在构造子类的,析构顺序相反
-
-
多继承中构造顺序问题
-
任何构造顺序问题都和初始化参数列表无关
-
构造顺序和继承顺序一致
-
class A
{
public:
A() { cout << "A"; }
~A() { cout << "A"; }
protected:
};
class B :public A
{
public:
B() { cout << "B"; }
~B() { cout << "B"; }
};
int main() {
B b;
/*
打印结果
ABBA
*/
return 0;
}
这里的单继承很好分析,要记住先构造的后析构就行了,我们下面来看多继承
class A
{
public:
A() { cout << "A"; }
~A() { cout << "A"; }
protected:
};
class C
{
public:
C() { cout << "C"; }
~C() { cout << "C"; }
};
class D
{
public:
D() { cout << "D"; }
~D() { cout << "D"; }
};
//构造顺序和继承顺序一致
class F :public C, public A, public D
{
public:
//任何构造顺序问题,都和初始化参数列表无关
F() { cout << "F"; }
~F() { cout << "F"; }
};
int main() {
F f;
/*
打印结果
CADFFDAC
*/
return 0;
}
这里呢我们需要记住继承中构造的顺序是与继承的顺序有关,例如上述例子,F类先继承C类然后继承A类最后继承D类,所以构造的顺序就是先调用C类的构造函数,然后调用A类的构造函数,最后调用D类的构造函数,最后的最后就是自己了,然后析构的顺序与构造顺序相反就行了