继承,C++
body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
|
——构造函数
——析构函数
——用户重载的new 、delete运算符
operator new /operator delete
——用户重载的=运算符
——友元关系
|
派生方式:
|
C++中,利用基类派生其子类(派生类)的基本格式为:
class 派生类名:派生方式 基类名
{
private:
新增私有成员列表;
public:
新增公开成员列表;
};
|
|
● 通过继承,派生类自动得到了除基类私有成员以外的其它所有数据成员和成员函数,在派生类中可以直接访问。
● private成员是私有成员,只能被本类的成员函数所访问,派生类和类外都不能访问。
● public成员是公有成员,在本类、派生类和外部都可访问
● protected成员是保护成员,只能在本类和派生类中访问。是一种区分血缘关系内外有别的成员。
● 派生类的访问权限规则如下:
1.不管是什么继承方式,子类中都不能访问父类的私有成员。
2.子类内部除了基类的私有成员不可以访问外,其他的都可以访问。
3.除了公有继承基类中的公有成员,子类对象可以访问外,其他的,子类对象一律不能访问 。
|
|
#include <iostream>
using namespace std;
class Point
{
public:
Point(int ix=0,int iy=0):_ix(ix),_iy(iy)
{ cout<<"Point(int,int)"<<endl; }
int getx()const
{ return _ix; }
int gety()const
{ return _iy; }
void display()
{ cout<<"("<<_ix<<","<<_iy<<")"<<endl; }
private:
int _ix;
int _iy;
};
class Point3D:public Point
{
public:
Point3D(int ix,int iy,int iz):Point(ix,iy),_iz(iz)
{ cout<<"Point3D(int,int,int)"<<endl; }
void display() //隐藏了基类中的同名函数
{
cout<<"("<<getx()<<","<<gety()<<","<<_iz<<")"<<endl; //不能直接访问基类的私有成员
}
private:
int _iz;
};
int main()
{
Point3D point3d(1,2,3);
point3d.display();
return 0;
}
|
#include <iostream>
using namespace std;
class Point
{
public:
Point(int ix=0,int iy=0):_ix(ix),_iy(iy)
{ cout<<"Point(int,int)"<<endl; }
int getx()const
{ return _ix; }
protected:
int gety()const
{ return _iy; }
public:
void display()
{ cout<<"("<<_ix<<","<<_iy<<")"<<endl; }
private:
int _ix;
int _iy;
};
class Point3D:private Point
{
public:
Point3D(int ix,int iy,int iz):Point(ix,iy),_iz(iz)
{ cout<<"Point3D(int,int,int)"<<endl; }
void display() //隐藏了基类中的同名函数
{
cout<<"("<<getx()<<","<<gety()<<","<<_iz<<")"<<endl; //不能直接访问基类的私有成员
}
private:
int _iz;
};
int main()
{
Point point(1,2);
//point.gety(); //protected修饰的成员不能通过对象访问
point.display();
Point3D point3d(1,2,3);
point3d.display();
return 0;
}
|
|
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,…,继承方式n 基类名n
{
private:
新增私有成员列表;
public:
新增公开成员列表;
};
例如,从A类和B类派生出C类的方式如下:
class C : public A, public B
{
//……
};
|
|
class 派生类名 : virtual 派生方式 基类名
{
//类定义
};
|
|
虚基派生和多基派生带来的二义性有些细微的差别:
1、多基派生的二义性主要是成员名的二义性,通过加作用域限定符来解决。
2、虚基派生的二义性则是共同基类成员的多重拷贝带来的存储二义性,使用virtual派生来解决
另外,二义性的检查是在访问权限检查之前进行的,因此,成员的访问权限是不能消除二义性的
|
|
//多基派生产生的二义性
#include <iostream>
using namespace std;
class A
{
public:
void print()
{ cout<<"A::print()"<<endl; }
};
class B
{
public:
void print()
{ cout<<"B::print()"<<endl; }
};
class C:public B,public A
{
public:
void display()
{
cout<<"C::display()"<<endl;
A::print(); //解决名字冲突二义性
B::print();
}
void print()
{
cout<<"C::print()"<<endl;
}
};
int main()
{
C c;
c.display();
c.A::print(); // 调用A类额的print
c.print() // 默认调用类C里面的
return 0;
}
//
C::display()
A::print()
B::print()
A::print()
C::print()
|
//虚基派生产生的二义性
#include <iostream>
using namespace std;
class A
{
public:
A(int x=0):_x(x)
{ cout<<"A(int)"<<endl; }
void setx(int x)
{ _x=x; }
void print()
{ cout<<"A::_x="<<_x<<endl; }
protected:
int _x;
};
//存储二义性问题,采用虚继承的方式解决
class B:virtual public A {};
class C:virtual public A {};
class D:public B,public C{};
int main()
{
D d;
cout<<"sizeof(C)="<<sizeof(C)<<endl;
cout<<"sizeof(B)="<<sizeof(B)<<endl;
cout<<"sizeof(D)="<<sizeof(d)<<endl; //不加virtual结果是 4、4、8
d.setx(5); //不加virtual这里产生二义性,编译不通过
d.print(); //virtual派生,在D中只有一个版本,不会产生二义性
return 0;
}
|
|
派生类名(总参数表): 基类构造函数(参数表)
{
//函数体
};
|
|
1、完成对象所占整块内存的开辟,由系统在调用构造函数时自动完成。
2、调用基类的构造函数完成基类成员的初始化。
3、若派生类中含对象成员、const成员或引用成员,则必须在初始化表中完成其初始化。
4、派生类构造函数体执行。
|
|
#include <iostream>
using namespace std;
//当派生类显示定义了构造函数,而基类没有显示定义构造函数,
//如果创建派生类对象,此时自动调用基类的默认构造函数
class Base
{
public:
Base()
{ cout<<"Base()"<<endl; }
private:
int _ix;
};
class Derived:public Base
{
public:
Derived()
{ cout<<"Derived()"<<endl; }
Derived(int ix)
{ cout<<"Derived(int)"<<endl; }
};
int main()
{
Derived d1(5);
return 0;
}
|
#include <iostream>
using namespace std;
//当派生类没有显示提供任何构造函数,而基类显示提供了有参构造函数,那
//么在创建派生类对象时,要求基类必须显示的提供一个默认构造函数
class Base
{
public:
Base()
{ cout<<"Base()"<<endl; }
Base(int x)
{ cout<<"Base(int)"<<endl; }
private:
int _x;
};
class Derived:public Base{}; //系统会提供默认的构造函数
int main()
{
Derived d1;
return 0;
}
|
#include <iostream>
using namespace std;
//当派生类和基类都显示定义了构造函数
//在创建派生类对象时,如果希望调用基类的有参
//构造函数,则需要在派生类构造函数的初始化列表
//之中显示的调用基类的构造函数
class Base
{
public:
Base()
{ cout<<"Base()"<<endl; }
Base(int x):_x(x)
{ cout<<"Base(int)"<<endl; };
private:
int _x;
};
class Derived:public Base
{
public:
Derived():Base()
//显示调用Base的默认构造函数,可写可不写
{ cout<<"Derived()"<<endl; }
Derived(int x):Base(x) //显示调用基类的有参构造函数
{ cout<<"Derived(int)"<<endl; }
};
int main()
{
Derived d1;
Derived d2(1);
return 0;
}
|
★执行顺序是先执行派生类的析构函数,再执行基类的析构函数,这和执行构造函数时的顺序正好相反。
|
#include <iostream>
using namespace std;
class A
{
public:
A(int x=0):_x(x)
{ cout<<"A::A(int)"<<endl; }
~A()
{ cout<<"A::~A()"<<endl; }
private:
int _x;
};
class B
{
public:
B()
{ cout<<"B::B()"<<endl; }
~B()
{ cout<<"B::~B()"<<endl; }
};
|
class C:public A
{
public:
C(int x,int y):A(x),b(),_y(y)
{ cout<<"C::C(int,int)"<<endl; }
~C()
{ cout<<"C::~C(int,int)"<<endl; }
private:
int _y;
B b;
};
int main()
{
C c(1,2);
return 0;
}
|
|
派生类名(总参数表):基类名1(参数表1),基类名2(参数表2),……,基类名N(参数表N)
{
//函数体
}
|
|
oversee 隐藏:父子类,函数名称相同,不带virtual关键字的函数
override 覆盖:父子类,函数的名称、返回值类型、参数的类型个数都相同有virtual关键字
overload 重载:同一个类,函数名称相同,参数不同(类型,顺序,个数)
|
|
#include <iostream>
using namespace std;
class Base
{
public:
int func(int i)
{ return _ix; }
protected:
int _ix;
};
class Derived : public Base
{
public:
int func()
{
_ix=9; //访问派生类的_ix,发生了隐藏
Base::_ix = 22;
//如果要访问基类的同名成员,必须使用基类的类名
return _ix;
}
void display()
{
cout<<"Derived::_ix = "<<_ix<<endl;
cout<<"Base::_ix = "<<Base::_ix<<endl;
}
private:
int _ix;
};
int main()
{
Derived d;
//cout<<d.func(3)<<endl;
//和基类同名的函数,基类函数发生隐藏,要调用只能加上基类类名
cout<<d.func()<<endl;
cout<<d.Base::func(3)<<endl;
d.display();
return 0;
}
|
//继承“是一个”
#include <iostream>
using namespace std;
class Eye
{
public:
void look()
{ cout<<"Eye::lock()"<<endl; }
};
class Nose
{
public:
void smell()
{ cout<<"Nose::smell()"<<endl; }
};
class Mouth
{
public:
void eat()
{ cout<<"Mouth::eat()"<<endl; }
};
class Ear
{
public:
void listen()
{ cout<<"Ear::listen()"<<endl; }
};
class Head:public Eye,public Nose,public Mouth,public Ear{};
int main()
{
Head head;
head.look();
head.smell();
head.listen();
head.eat();
return 0;
}
|
//组合 “有一个”
#include <iostream>
using namespace std;
class Eye
{
public:
void look()
{ cout<<"Eye::lock()"<<endl; }
};
class Nose
{
public:
void smell()
{ cout<<"Nose::smell()"<<endl; }
};
class Mouth
{
public:
void eat()
{ cout<<"Mouth::eat()"<<endl; }
};
class Ear
{
public:
void listen()
{ cout<<"Ear::listen()"<<endl; }
};
class Head
{
public:
void look()
{ _eye.look(); }
void smell()
{ _nose.smell(); }
void listen()
{ _ear.listen(); }
void eat()
{ _mouth.eat(); }
private:
Eye _eye;
Nose _nose;
Mouth _mouth;
Ear _ear;
};
int main()
{
Head head;
head.look();
head.smell();
head.listen();
head.eat();
return 0;
}
|
| 从逻辑上说,继承是一种a kind of的关系(AKO)或者说IS-A(“是一个”),汽车是车,因此,汽车类可以从普通的车类继承而来,轮子类就不能从汽车类继承来,轮子是汽车的一个部件,轮子可以作为汽车类的对象成员,这就是“组合”(compositioin A has B)。 |
|
#include <iostream>
using namespace std;
class Point
{
public:
Point(int ix,int iy):_ix(ix),_iy(iy)
{ cout<<"Point(int,int)"<<endl; }
void display()
{ cout<<"("<<_ix<<","<<_iy<<")"<<endl; }
protected:
int _ix;
int _iy;
};
class Point3D : public Point
{
public:
Point3D(int ix,int iy,int iz):Point(ix,iy),_iz(iz)
{ cout<<"Point3D(int,int,int)"<<endl; }
void display()
{ cout<<"("<<_ix<<","<<_iy<<","<<_iz<<")"<<endl; }
private:
int _iz;
};
|
int main()
{
Point point(1,2);
point.display();
Point3D point3d(3,4,5);
point3d.display();
cout<<endl;
point = point3d; //派生类对象为基类对象赋值
point.display();
Point & ref = point3d; //派生类对象初始化基类对象引用
ref.display();
Point * p = & point3d; //派生类对象地址为基类指针赋值
p->display();
return 0;
}
|
|
#include <iostream>
#include <string.h>
using namespace std;
class Base
{
public:
Base(const char * pbuf):_pbuf(new char[strlen(pbuf)+1])
{
strcpy(_pbuf,pbuf);
cout<<"Base(const char *)"<<endl;
}
Base(const Base & rhs):_pbuf(new char[strlen(rhs._pbuf)+1])
{
strcpy(_pbuf,rhs._pbuf);
cout<<"Base(const Base &)"<<endl;
}
Base & operator = (const Base & rhs)
{
cout<<"Base & operator = (const Base &)"<<endl;
if(this != &rhs)
{
delete []_pbuf;
_pbuf = new char[strlen(rhs._pbuf)+1];
strcpy(_pbuf,rhs._pbuf);
}
return *this;
}
void display()
{
cout<<_pbuf<<endl;
}
private:
char * _pbuf;
};
class Derived : public Base
{
public:
Derived(const char * pbuf):Base(pbuf)
{ cout<<"Derived(const char *)"<<endl; }
};
int main()
{//当派生类没有定义复制构造函数,而基类显示的定义了复制构造函数,
//则派生类对象之间进行复制时,会自动调用基类复制构造函数
Derived d1("hello");
d1.display();
Derived d2("world");
d2.display();
cout<<endl;
d1=d2; //派生类使用缺省的赋值构造函数,基类调用用户定义的赋值构造函数
d1.display();
d2.display();
cout<<endl;
Derived d3 (d1); //派生类使用缺省的拷贝构造函数,基类调用用户定义的拷贝构造函数
d3.display();
return 0;
}
//如果派生类不显示调用基类的拷贝构造函数和赋值构造函数,执行结果就是这样的
|
#include <iostream>
#include <string.h>
using namespace std;
class Base
{
public:
Base():_pbuf(new char[1])
{ cout<<"Base()"<<endl; }
Base(const char * pbuf):_pbuf(new char[strlen(pbuf)+1])
{
strcpy(_pbuf,pbuf);
cout<<"Base(const char *)"<<endl;
}
Base(const Base & rhs):_pbuf(new char[strlen(rhs._pbuf)+1])
{
strcpy(_pbuf,rhs._pbuf);
cout<<"Base(const Base &)"<<endl;
}
Base& operator =(const Base & rhs)
{
cout<<"Base & operator = (const Base &)"<<endl;
if(this != &rhs)
{
delete []_pbuf;
_pbuf = new char[strlen(rhs._pbuf)+1];
strcpy(_pbuf,rhs._pbuf);
}
return *this;
}
~Base()
{
delete []_pbuf;
cout<<"~Base()"<<endl;
}
void display()
{ cout<<_pbuf; }
private:
char * _pbuf;
};
class Derived : public Base
{
public:
Derived(const char* pbuf,const char* pbuf2):Base(pbuf),_pbuf2(new char[strlen(pbuf2)+1])
{
cout<<"Derived(const char *,const char *)"<<endl;
strcpy(_pbuf2,pbuf2);
}
Derived(const Derived & rhs):Base(rhs),_pbuf2(new char[strlen(rhs._pbuf2)+1])
{//显示调用基类的复制构造函数
cout<<"Derived(const char *,const char *)"<<endl; //这里写错了,导致最后一个结果显示这里了。
strcpy(_pbuf2,rhs._pbuf2);
}
Derived & operator= (const Derived & rhs)
{
cout<<"Derived & operator = (const Derived &)"<<endl;
if(this != &rhs);
{
Base::operator = (rhs); //显示调用基类的赋值运算符函数
delete []_pbuf2;
_pbuf2=new char[strlen(rhs._pbuf2)+1];
strcpy(_pbuf2,rhs._pbuf2);
}
return *this;
}
~Derived()
{
delete []_pbuf2;
cout<<"~Derived()"<<endl;
}
void display()
{
Base::display();
cout<<_pbuf2<<endl;
}
private:
char * _pbuf2;
};
int main()
{//当派生类显示定义了复制构造函数,而基类显示定义了复制构造函数,
//则派生类对象之间进行复制时,不会自动调用基类的复制构造函数,
//必须在派生类的复制构造函数之中显示调用基类的复制构造函数。
Derived d1("hello",",world");
d1.display();
Derived d2("shenzhen",",wangdao");
d2.display();
cout<<endl;
d1=d2;
d1.display();
d2.display();
cout<<endl;
Derived d3=d1;
d3.display();
return 0;
}
|
继承,C++的更多相关文章
- javaScript的原型继承与多态性
1.prototype 我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接,只不过这种链接是不可见,给人们的感觉 ...
- JavaScript的继承实现方式
1.使用call或apply方法,将父对象的构造函数绑定在子对象上 function A(){ this.name = 'json'; } function B(){ A.call(this); } ...
- javascript中的继承与深度拷贝
前言 本篇适合前端新人,下面开始...... 对于前端新手来说(比如博主),每当对js的对象做操作时,都是一种痛苦,原因就是在于对象的赋值是引用的传递,并非值的传递,虽然看上去后者赋值给了前者,他们就 ...
- 谈谈一些有趣的CSS题目(四)-- 从倒影说起,谈谈 CSS 继承 inherit
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
- JS继承类相关试题
题目一: //有关于原型继承的代码如下:function Person(name) { this.name = name;}Person.prototype = { getName : f ...
- JS继承之寄生类继承
原型式继承 其原理就是借助原型,可以基于已有的对象创建新对象.节省了创建自定义类型这一步(虽然觉得这样没什么意义). 模型 function object(o){ function W(){ } W. ...
- JS继承之借用构造函数继承和组合继承
根据少一点套路,多一点真诚这个原则,继续学习. 借用构造函数继承 在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术( ...
- JS继承之原型继承
许多OO语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支 ...
- 深入浅出JavaScript之原型链&继承
Javascript语言的继承机制,它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instanc ...
- 如果你也会C#,那不妨了解下F#(7):面向对象编程之继承、接口和泛型
前言 面向对象三大基本特性:封装.继承.多态.上一篇中介绍了类的定义,下面就了解下F#中继承和多态的使用吧.
随机推荐
- firewalld管理防火墙常用命令
1.查看防火墙的状态 [root@localhost HMK]# firewall-cmd --state 查看防火墙的运行状态 not running [root@localhost HMK]# s ...
- Go语言学习之3 流程控制、函数
主要内容: 1. strings和strconv使用2. Go中的时间和日期类型3. 指针类型4. 流程控制5. 函数详解 1. strings和strconv使用 //strings . strin ...
- Go语言学习之2 包、函数、常量、数据类型、字符操作
第一部分:基本数据类型和操作符 1. 文件名&关键字&标识符 (1)所有go源码以.go结尾 (2)标识符以字母或下划线开头,大小写敏感,比如: a. boy b. Bo ...
- OnSen UI结合AngularJs打造”美团"APP底部导航栏 --Hybrid App
1.页面效果图:(点击底部导航按钮,可切换到不同的页面) 演示地址:http://www.nxl123.cn/bokeyuan/2018080301/meiTuanDemo/ 2.项目目录结构 3.核 ...
- vux, vue如何控制微信自带的返回按钮,让其返回其他页面?
<script> import { mapState } from 'vuex' export default{ name: 'clockFx', data () { return { } ...
- yii框架中获取添加数据后的id值
Yii::$app->db->createCommand()->insert('month4_user',['openid'=>$openid,'integ'=>0])- ...
- 『PyTorch』第三弹_自动求导
torch.autograd 包提供Tensor所有操作的自动求导方法. 数据结构介绍 autograd.Variable 这是这个包中最核心的类. 它包装了一个Tensor,并且几乎支持所有的定义在 ...
- vim操作(待补充)
:wq 存盘 + 退出 (:w 存盘, :q 退出) :e 打开新文件 :q 退出 h.j.k.l,分别控制光标左.下.上.右移一格. 按Ctrl+B:屏幕往后移动一页.[常用] 按Ctrl+F:屏幕 ...
- ubuntu chmod命令的使用
我推荐的地址:http://blog.163.com/bluesky_07_06_1/blog/static/164440083201161451735773/ 这个非常的牛逼.
- poj-2142-exgcd/解的和最小
The Balance Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 8816 Accepted: 3833 Descr ...