C++基础-(继承)
2016-11-14 本文已影响11人
I踏雪寻梅
C++ 基础
概念及工方式
- 保持已有类的特性而构造新类的过程称为继承。
- 在已有类的基础上新增自己的特性而产生新类的过程称为派生。
- 被继承的已有类称为基类(或父类)。
- 派生出的新类称为派生类(或子类)
继承的目的
- 实现代码重用
- 派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造
派生类的构造
- 派生类可以直接访问基类的保护数据成员
- 只以接口作沟通。即使基类与子类也不例外。这正是类能够发挥其生命力的原因所在。
- 在构造一个子类时,完成其基类部分的构造由基类的构造函数去做
继承的访问权限
- 不同继承方式的影响主要体现在:
- 派生类成员对基类成员的访问权限
- 通过派生类对象对基类成员的访问权限
- 三种继承方式
- 公有继承
- 私有继承
- 保护继承
公有继承
- 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
- 通过派生类的对象只能访问基类的public成员。
私有继承
- 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
- 通过派生类的对象不能直接访问基类中的任何成员。
保护继承
- 基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可直接访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
- 通过派生类的对象不能直接访问基类中的任何成员
默认的继承是私有
private 和protect的区别
- 在单个类中,protected和private没有什么区别。
- 但在继承关系中,基类的private成员不但对应用程序隐藏,
- 甚至对派生类也隐藏。而基类的保护成员则只对应用程序隐藏,而对派生类则毫不隐瞒。
继承
- 继承
- 抽出共性,重新分配,代码可重用
- 继承可以使我们用一种简单的方式来描述事物
#include <iostream>
using namespace std;
class Shop
{
public:
int m_a;
void sailDailyGoods()
{
cout<<"卖日用品"<<endl;
return;
}
};
class Market:public Shop
{
public:
void saleFood()
{
m_a=110;
cout<<"卖食物"<<endl;
return;
}
};
class SuperMarket:public Market
{
public:
void pay()
{
cout<<"代缴水电费"<<endl;
return;
}
};
int main(int argc,char**argv)
{
SuperMarket sm;
sm.sailDailyGoods();
sm.saleFood();
sm.pay();
}
- 三种继承方式
- 公有继承
- 私有继承
- 保护继承
- 派生类可以直接访问
#include <iostream>
using namespace std;
class shop
{
int m_a;
public:
shop(int a/*=110*/)
{
m_a=a;
cout<<"shop construct"<<m_a<<endl;
}
~shop()
{
cout<<"析构shop"<<endl;
}
/*
shop()
{
m_a=333;
}*/
};
class test
{
public:
test()
{
cout<<"test construct"<<endl;
}
~test()
{
cout<<"析构test"<<endl;
}
};
class Market:public shop//继承重用代码
{
test m_t;
public:
Market():shop(120)
{
cout<<"Market construct"<<endl;
}
~Market()
{
cout<<"析构Market"<<endl;
}
};
int main(int argc,char**argv)
{
Market m;
cout<<"===="<<endl;
}
//shop construct120
//test construct
//Market construct
//====
//析构Market
//析构test
//析构shop
#include<iostream>
using namespace std;
class Shop
{
protected:
int m_a;
public:
void saleDaliyGoods()
{
cout<<"卖日用品"<<endl;
return;
}
};
class Market:public Shop
{
public:
void saleFood()
{
m_a=10;
cout<<"食品"<<m_a<<endl;
return;
}
};
int main(int argc,char **argv)
{
Market m;
// m.m_a=10;
m.saleDaliyGoods();
m.saleFood();
}
//用protected ,则在Market中可以访问,但是在int main中,m.m_a=10;是错误的
#include<iostream>
using namespace std;
class Shop
{
public:
Shop()
{
cout<<"Shop construct"<<endl;
}
};
class Market:public Shop
{
public:
Market()
{
cout<<"Market construct"<<endl;
}
};
int main()
{
Market m;
}
//结果为:
//shop construct
//Market construct
#include<iostream>
using namespace std;
class Shop
{
int m_a;
public:
Shop(int a)
{
m_a=a;
cout<<"Shop construct"<<m_a<<endl;
}
};
class Market:public Shop
{
public:
Market():Shop(222)
{
cout<<"Market construct"<<endl;
}
};
int main()
{
Market m;
}
//结果为:
//Shop construct222
//Market construct
#include<iostream>
using namespace std;
class Shop
{
int m_a;
public:
Shop(int a=111)
{
m_a=a;
cout<<"Shop construct"<<m_a<<endl;
}
};
class Market:public Shop
{
public:
Market():Shop()
{
cout<<"Market construct"<<endl;
}
};
int main()
{
Market m;
}
//结果为:Shop construct111
//Market construct
#include<iostream>
using namespace std;
class Shop
{
int m_a;
public:
Shop(int a)
{
m_a=a;
cout<<"Shop construct"<<m_a<<endl;
}
Shop()
{
m_a=333;
cout<<"Shop construct"<<m_a<<endl;
}
};
class Market:public Shop
{
public:
Market():Shop()
{
cout<<"Market construct"<<endl;
}
};
int main()
{
Market m;
}
//结果为:
Shop construct333
//Market construct
赋值兼容规则
- 赋值兼容规则中所指的替代包括以下的情况:
- 派生类的对象可以赋值给基类对象。
- 派生类的对象可以初始化基类的引用。
- 派生类对象的地址可以赋给指向基类的指针。
重写虚函数,实现多态(多态)
- 多态是C++的重要概念,是面向对象设计理念的精华, 大型的C++软件项目和大型的系统实现中,多态是必不可少的,可以减轻系统升级、维护、调试的工作量和复杂度.
多态的条件
- C++多态是通过虚函数实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类接口的做法称为覆盖或重写
- 子类需要公有继承于父类
- 定义父类的指针或引用,然后通过指针或引用去调用相应的虚函数
虚函数
- 成员函数之前加上 virtua
关键字之后,就是虚成员函数
判断子类的成员函数是否为虚函数
- 该函数是否与基类的虚函
数相同的函数名- 该函数是否与基类的虚函数有相同的参数个数及相同的对应参数类型
- 该函数是否与基类的虚函数有相同的返回值或者满足赋值兼容规则的指针、引用的返回值
多态与非多态的区别
- 多态与非多态的区别就是函数地址是早绑定还是晚绑定。如果函数的调用在编译期间就可以确定函数的调用地址,并生产代码,就是静态的也就是早绑定是非多态的。而如果函数的调用地址不能在编译期间确定,需要在运行时才确定,这就是晚绑定也称动态联编,是多态的
#include <iostream>
using namespace std;
class B0
{
public:
virtual void display()
{
cout<<"B0 Display"<<endl;
}
};
class B1:public B0
{
public:
void display()
{
cout<<"B1 Display"<<endl;
}
};
class B2:public B1
{
public:
void display()
{
cout<<"B2 Display"<<endl;
}
};
int main()
{
/*
B0 b0;b0.display();
B1 b1;b1.display();
B2 b2;b2.display();
B2 b3;b3.B1::display();*/
B0 b0,*p0;
p0=&b0;
p0->display();
B1 b1;
p0=&b1;//一个函数表现出不同的形态
p0->display();
B2 b2;
p0=&b2;
p0->display();
}
重载与重写的区别(重写重要)
- 重载overload:函数名相同,参数列表不同,重载只是在同类的内部存在,并且不能靠返回值来判断
- 重写overwrite:也称覆盖,子类重新定义父类中的同名函数。函数特征相同,单数具体实现不同,基类的函数要有关键字virtual,主要是在继承关系中出现。
- 多态实现原理
- 每个含有虚函数的类,编译器都会为他自动生成一个虚表,表中每一个元素都是指向虚函数的地址。
- 虚指针:编译器会为含有虚函数的类增加一个成员,是指向该虚函数的指针,每一个由此的派生出来的类都活有一个虚指针。
- 函数的调用地址在编译期间就能决定,那就是非多态。如果在运行时才能确定,就是多态的。
- 继承为了实现代码重用。
- 多态为了实现接口重用。
#include <iostream>
using namespace std;
/*class B0
{
public:
virtual void display()
{
cout<<"B0 Display"<<endl;
}
};
class B1:public B0
{
public:*/
/* void display()
{
cout<<"B1 Display"<<endl;
}*/
/*};
int main()
{*/
/*
B0 b0;b0.display();
B1 b1;b1.display();
B2 b2;b2.display();
B2 b3;b3.B1::display();*/
/* B0 b0,*p0;
p0=&b0;
p0->display();
B1 b1;
p0=&b1;//一个函数表现出不同的形态
p0->display();
}*/
class B0
{
public:
virtual void display()=0;//纯虚函数
};
class B1:public B0
{
public:
void display()
{
cout<<"B1 Display"<<endl;
}
};
int main()
{
/*
B0 b0;b0.display();
B1 b1;b1.display();
B2 b2;b2.display();
B2 b3;b3.B1::display();*/
B0 *p0;
B1 b1;
p0=&b1;//一个函数表现出不同的形态
p0->display();
}
//B1 Display
- 共态的使用
#include <iostream>
using namespace std;
class person
{
public:
int id;
};
class A:virtual public person
{
public:
void performForA()
{
cout<<"呆,傻,逗,痴"<<endl;
}
};
class B:virtual public person
{
public:
void performForB()
{
cout<<"浪,骚,荡,贱"<<endl;
}
};
class sb:public A,public B
{
};
int main()
{
sb jxb;
jxb.performForA();
jxb.performForB();
jxb.id=3;
person *p=&jxb;
}
继承顺序
- 任何虚拟基类的构造函数按照它们被继承的顺序构造
- 任何非虚拟基类的构造函数按照它们被继承的顺序构造;
- 任何成员对象的构造函数按照它们声明的顺序调用;
类自己的构造函数。
- 如果两个父类。。。。。。。。。。。。。。。。
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A construct"<<endl;
}
~A()
{
cout<<"析构A"<<endl;
}
};
class B
{
public:
B()
{
cout<<"B construct"<<endl;
}
~B()
{
cout<<"析构B"<<endl;
}
};
class C
{
public:
C()
{
cout<<"C construct"<<endl;
}
~C()
{
cout<<"析构C"<<endl;
}
};
class D:public A,public B,virtual public C
{
public:
D()
{
cout<<"D construct"<<endl;
}
~D()
{
cout<<"析构D"<<endl;
}
};
int main()
{
D d;
}/*
C construct
A construct
B construct
D construct
析构D
析构B
析构A
析构C*/