概述
一、继承/派生
1. 作用:
继承的目的是"延用旧功能",派生的目的是"增加新功能"。
人类:属性(名字,年龄)
行为(吃,睡)
教师:属性(名字,年龄,薪水)
行为(吃,睡,教学)
学生:属性(名字,年龄,成绩)
行为(吃,睡,学习)
class Human {
name, age, void eat(), void sleep();
};
class Teacher {
...//Human 类的全部内容
salary, void teach()
};
class Student {
...//Human 类的全部内容
score, void learn()
};
人类
---> ("基类/父类")
/
教师 学生
---> ("派生类/子类")
2. 语法规则:
class 类名 [:继承列表] {
...//成员
};
3. "继承列表":继承方式 父类1类名, 继承方式 父类2类名, ...
4. "继承方式":公有 public;保护 protected;私有 private;
"访问权限限定符":
·public:
·protected: //保护类型的访问权限,仅子类可以访问
·private:
【父类访问权限】
|
"公有继承"
|
"保护继承"
|
"私有继承"
·---------------+---------------+---------------+------------·
| /*公有权限*/
|
公有
|
保护
|
私有
|
| /*保护权限*/
|
保护
|
保护
|
私有
|
| /*私有权限*/
|
无法访问
|
无法访问
|
无法访问
|
·---------------+---------------+---------------+------------·
/** 代码演示 **/
#include <iostream>
using namespace std;
class A {
public: int pub; //公有
protected: int pro; //保护
private: int pri; //私有
};
class B :public A {
void foo() { pub; pro; /*pri;*/ }
};
class C :public B {
void bar() { pub; pro; /*pri;*/ }
};
int main() {
cout << "sizeof(A): " << sizeof(A) << endl;//12
cout << "sizeof(B): " << sizeof(B) << endl;//12
cout << "sizeof(C): " << sizeof(C) << endl;//12
return 0;
}
5. 继承的内存分布:
按地址由低到高先放父类对象的成员,再放子类对象的成员。
·-----------· 低地址
| name 4 | |
| age 4 | |
·-----------· |
| salary | |
| 8 | |
·-----------· 高地址
6. 子类对象显式调用父类的构造函数
class A {};
class B :public A {
A() {}
A(int) {}
};
class B :public A {
B(int b): 显式调用父类的构造函数,子类成员构造 {}
};
B b;
继承对象的生命周期:
1)创建:
<1>先父类的对象自上而下依次创建;
<2>调用父类的构造函数
<3>子类成员对象自上而下依次创建;
<4>调用子类的构造函数
2)销毁:
<1>调用子类的析构函数
<2>子类成员对象自下而上依次销毁
<3>调用父类的析构函数
<4>父类成员对象自下而上依次销毁
/** 代码演示 **/
#include <iostream>
using namespace std;
class Human {
public:
Human(): name("无名"), age(0) {}
Human(const string & n, int a = 0): name(n), age(a) {}
void eat(const string & what) {
cout << name << " is eating " << what << "..." << endl;
}
void sleep(int hour) {
cout << name << " was slept" << hour << " hour..." << endl;
}
protected:
string name;
private:
int age;
};
class Teacher :public Human {
public:
Teacher(const string & name, int a=1, double s=0.0): Human(name, a), salary(s) { //Human(name, a) 调用父类的构造函数
//初始化后再赋值,这两句等同于 Human(name, a)
/* this->name = name;
age = a; */
}
void teach(const string & s) {
cout << name << " is teaching " << s << "..." << endl;
}
private:
double salary;
};
int main(void) {
Teacher t1("张飞");
t1.eat("包子");
t1.teach("C++");
cout << "sizeof(t1):" << sizeof(t1) << endl; //16
return 0;
}
7. 子类与父类的赋值规则
A a;
B b;
a = b; //合法, 子 ----> 赋值给 ----> 父 ok
b = a; //不合法
1)子类对象可以赋值给父类对象,子类对象的成员将被遗弃;
2)父类对象不能赋值给子类对象。
8. 向下/向上造型(cast)类型转换 "赋值=号从右值往左值看,仅向上合法!"
1)子类对象的地址可以赋值给父类对象的指针,反之不合法;
A a;
B b;
A *pa = &b; //合法, 子对象地址 ----> 赋值给 ----> 父对象指针
B *pb = &a; //不合法
2)子类对象的引用可以赋值给父类对象的引用,反之不合法;
A a;
B b;
A & ra = b; //合法, 子对象引用 ----> 赋值给 ----> 父对象引用
B & rb = a; //不合法
总结:编译器认为自大至小的自定义类类型转换是安全的,反之是不安全的。
/** 代码演示 **/
#include <iostream>
using namespace std;
class Human {
public:
Human(): name("无名"), age(0) {}
Human(const string & n, int a = 0): name(n), age(a) {}
void eat(const string & what) {
cout << name << " is eating " << what << "..." << endl;
}
void sleep(int hour) {
cout << name << " was slept " << hour << " hour..." << endl;
}
protected:
string name;
private:
int age;
};
class Teacher :public Human {
public:
Teacher(const string & name, int a=1, double s=0.0): Human(name, a), salary(s) {
//初始化后再赋值,这两句等同于 Human(name, a)
/* this->name = name;
age = a; */
}
void teach(const string & s) {
cout << name << " is teaching " << s << "..." << endl;
}
private:
double salary;
};
int main(void) {
Teacher t1("张飞");
t1.eat("包子");
t1.teach("C++");
cout << "sizeof(t1):" << sizeof(t1) << endl; //16
Human h1;
h1 = t1;
t1.sleep(2);
Human *ph;
ph = &t1; //合法,向上造型(从右值往左值看)
Teacher *pt;
// pt = &h1; //不合法
Human & rh = t1; //合法,向上造型
// Teacher & rt = h1; //不合法
return 0;
}
9. 隐藏:hidden
1)概念:
当子类中添加与父类"同名"的成员函数或成员变量时,使用子类的对象调用其父类的成员函数/对象,将无法被找到,此现象成为子类成员函数让父类成员函数造成隐藏。
/** 代码演示 **/
#include <iostream>
using namespace std;
class A {
public:
void foo() { cout << "A::foo()n"; }
private:
};
class B : public A { // B ->继承 A
public:
void foo(int i) { cout << "B::foo(int)n"; }
};
int main(void) {
B b;
b.foo(100); //B::foo(int)
b.foo(); //编译报错,被隐藏!!!
return 0;
}
2)解决办法:
<1>添加"作用域限定符"的方式来调用
B b;
b.foo(100); //B::foo(int)
b.A::foo(); //A::foo() 正确!!~
<2>使用"using名字空间声明在 public 下",将同名函数建立重载关系
class B : public A { // B ->继承 A
public:
using A::foo; //名字空间声明,foo()与foo(int)重载
void foo(int i) { cout << "B::foo(int)n"; }
};
10. 保护继承和私有继承的造型(cast)
1)保护继承和私有继承的子类地址,不能赋值给父类的指针;
2)保护继承和私有继承的子类对象,不能赋值给父类的引用;
/** 代码演示 **/
#include <iostream>
using namespace std;
class A {
public:
void foo() { cout << "A::foo()n"; }
};
class B : protected A {
};
class C : public B {
public:
C() { foo(); } //能调用foo
};
int main(void) {
B b;
b.foo(); // 错误,保护继承不能调用父类的成员函数
A *pa = &b; //不合法,保护继承向上造型不合法
pa->foo();
return 0;
}
/** 单继承的应用,代码演示 **/
#include <iostream>
using namespace std;
class Point { //点
public:
Point(int x, int y): m_x(x), m_y(y) {}
void draw() {
cout << "画点:(" << m_x << ", " << m_y << ")n";
}
protected:
int m_x;
int m_y;
};
class Circle : public Point {
public:
Circle(int x, int y, int r): Point(x, y), m_r(r) {}
void draw() {
cout << "画圆:(" << m_x << ", " << m_y << ", " << m_r
<< ")n";
}
private:
int m_r;
};
class Rectandle : public Point { //作业:实现矩形2种方法
};
int main(void) {
Point p1(100, 200);
Point p2(20, 55);
p1.draw();
p2.draw();
Circle c1(0, 0, 100);
Circle c2(10, 10, 10);
c1.draw();
c2.draw();
return 0;
}
二、多继承
1. 语法:
class A [:继承方式 类1, 继承方式 类2, ...] {
};
技术人员 经理 销售人员
/ /
项目经理 销售经理
苹果: 播放器 电脑 手机
| /
智能手机
/** 代码演示 **/
#include <iostream>
#include <string>
using namespace std;
class Player{ //播放器
public:
Player(const string & b): m_brand(b) { }
void play(const string & music) {
cout << m_brand << "正在播放" << music << "..." << endl;
}
private:
string m_brand;
};
class Computer { //电脑
public:
Computer(const string & os): m_os(os) { }
void run(const string & app) {
cout << m_os << "正在运行:" << app << "..." << endl;
}
private:
string m_os;
};
class Phone { //手机
public:
Phone(const string & n): m_number(n) {}
void call(const string & other) {
cout << m_number << "正在打电话给" << other << "..." << endl;
}
private:
string m_number;
};
class SmartPhone : public Player, public Computer, public Phone {
public:
SmartPhone(const string & brand, const string & os, const string
& numb): Player(brand), Computer(os), Phone(numb) {}
};
int main(void) {
Player p("爱国者");
p.play("小苹果.mp3");
Computer c("Windows");
c.run("CS.exe");
Phone phone("13843838438");
phone.call("10086");
SmartPhone sp("小米", "Android", "13988889999");
sp.play("Double tiger.mp3");
sp.run("Plants VS Zombi.app");
sp.call("10000");
SmartPhone *p_smartphone = &sp;
Player *p_player = &sp;
Computer *p_computer = &sp;
Phone *p_phone = &sp;
cout << &sp << endl; //0xbf959c6c
cout << p_smartphone << endl; //0xbf959c6c
cout << p_player << endl; //0xbf959c6c
cout << p_computer << endl; //0xbf959c70
cout << p_phone << endl; //0xbf959c74
// SmartPhone *p2 = p_phone; //不可以的
SmartPhone *sp2 = static_cast<SmartPhone*>(p_phone);
cout << "sp2 address: " << sp2 << endl;
sp2->run("Angry birds.app");
cout << "----------------------------n";
Phone & rphone = sp;
rphone.call("10010");
// SmartPhone & rsp = rphone; //不合法
SmartPhone & rsp = static_cast<SmartPhone&>(rphone);
rsp.play("小小.mp3");
return 0;
}
2. 说明:
多继承内存布局:按继承列表中的类的对象"依次自低地址向高地址排布"。1)在多继承中子类对象"地址"可以赋值给基类对象的"指针";
指针会自动偏移到基类对象的位置。
2)在多继承中子类"对象"可以赋值给基类对象的"引用"。
3)基类对象的地址或对象本身,要转换回子类对象的指针或引用,需要用到
static_cast 进行转换,并且不保证转换的正确性。
4)reinterpret_cast 在多继承中要慎用或不用。
p_smartphone--->+-----------+<----- p_player
| m_brand |
+-----------+<-----p_computer=&sp + sizeof(Player)
| m_os |
+-----------+<-----p_phone=&sp + sizeof(Player)
| m_number | + sizeof(Computer)
+-----------+
3. 多继承中标识符冲突的问题:
class B1 {
public:
int m_b;
};
class B2 {
public:
int m_b;
};
class C : public B1, public B2 {
void foo() {
//加作用域限定符可以避免冲突。
cout << m_b << endl; //error!!!
cout << B1::m_b << endl; // 合法
}
};
说明:
1)用作用域限定符可以解决冲突问题;
2)成员函数可以用名字空间声明(using namespace)解决冲突问题。
补充:
"匈牙利命名法"(主要用于C++当中):
标识符命名
m_... 成员变量
s_... 静态成员变量
g_... 无名字空间的全局变量
n 代表整形
p 代表指针
pp 代表指针的指针
c 代表字符
【示例】
m_pNumber; //成员变量
s_nCount; //静态成员变量,整形的。
/** 作业 **/
#include <iostream>
using namespace std;
class Point { //点
public:
Point(int x, int y): m_x(x), m_y(y) {}
void draw() {
cout << "画点:(" << m_x << ", " << m_y << ")n";
}
protected:
int m_x;
int m_y;
};
class Circle : public Point {
public:
Circle(int x, int y, int r): Point(x, y), m_r(r) {}
void draw() {
cout << "画圆:(" << m_x << ", " << m_y << ", " << m_r
<< ")n";
}
private:
int m_r;
};
class Rectandle : public Point { //作业:实现矩形2种方法
};
int main(void) {
Point p1(100, 200);
Point p2(20, 55);
p1.draw();
p2.draw();
Circle c1(0, 0, 100);
Circle c2(10, 10, 10);
c1.draw();
c2.draw();
return 0;
}
作业:
修改shape.cpp文件,实现两个新类:Rectandle(矩形),和Ellipse(椭圆)
/** 作业代码 **/
#include <iostream>
using namespace std;
class Point {
public:
Point(int x, int y): m_x(x), m_y(y) {}
void draw() {
cout << "画点:(" << m_x << ", " << m_y << ")n";
}
protected:
int m_x;
int m_y;
};
class Circle : public Point {
public:
Circle(int x, int y, int r): Point(x, y), m_r(r) {}
void draw() {
cout << "画圆:(" << m_x << ", " << m_y << ", "
<< m_r << ")n";
}
private:
int m_r;
};
class Rectandle : public Point {
public:
Rectandle(int x, int y, int i, int j)
: Point(x, y), m_rx(i), m_ry(j) {}
void draw() {
cout << "画长方形:((" << m_x << ", " << m_y << "),("
<< m_rx << ", " << m_ry << "))n";
}
private:
int m_rx;
int m_ry;
};
class Ellipse: public Point {
public:
Ellipse(int x, int y, int l, int w): Point(x, y), m_l(l), m_w(w) {}
void draw() { //(m_x, m_y), m_l, m_w
int x1,y1,x2,y2,x3,y3,x4,y4;
x1 = x3 = (m_l - m_x) / 2; //(x1, m_y), (x3, y3)
y3 = m_w - m_y;
x2 = x4 = (m_w - m_y) / 2; //(x2, m_x), (x4, y4)
y4 = m_l - m_x;
cout << "椭圆四顶点坐标:((" << x1 << ", " << m_y << "),("
<< x2 << ", " << m_x << "),("
<< x3 << ", " << y3 << "),("
<< x4 << ", " << y4 << "))" << endl;
}
private:
int m_l;
int m_w;
};
int main(void) {
Point p1(1, 3);
p1.draw();
Circle c1(1, 3, 5);
c1.draw();
Rectandle r1(1, 3, 2, 4);
r1.draw();
Ellipse e1(1, 1, 3, 6);
e1.draw();
return 0;
}
后补作业:
自定义Array类,成员数组使用指针形式重新做一遍。
class Array {
public:
... //自行添加
private:
int m_data[1000]; //将此数组换成指针
int m_count; //用来保存数组元素的个数
};
int main(void) {
Array arr1(2, 1); // {1,1}
arr1[1] = 2;
cout << "arr1 = " << arr1 << endl; //{1,2}
Array arr2(2, 3);
arr2[1] = 4;
arr2.push_back(5); //在数组末尾追加
cout << "" << arr2 << endl; //{3,4,5}
cout << "arr1+arr2 = " << arr1 + arr2 << endl; //{1,2,3,4,5}
return 0;
}
#include <iostream>
using namespace std;
class Point {
public:
Point(int x, int y): m_x(x), m_y(y) {}
void draw() {
cout << "画点:(" << m_x << ", " << m_y << ")n";
}
protected:
int m_x;
int m_y;
};
class Circle : public Point {
public:
Circle(int x, int y, int r): Point(x, y), m_r(r) {}
void draw() {
cout << "画圆:(" << m_x << ", " << m_y << ", "
<< m_r << ")n";
}
private:
int m_r;
};
class Rectandle : public Point {
public:
Rectandle(int x, int y, int w, int h):Point(x, y), m_width(w), m_height(h) {}
void draw() {
cout << "画矩形:((" << m_x << ", " << m_y << "),(" <<
m_width << ", " << m_height << "))" << endl;
}
protected:
int m_width;
int m_height;
};
class Ellipse: public Rectandle {
public:
Ellipse(int x, int y, int w, int h): Rectandle(x, y, w, h) {}
void draw() {
cout << "画椭圆:((" << m_x << ", " << m_y << "), " <<
m_width << ", " << m_height << ")" << endl;
}
};
int main(void) {
Point p1(1, 3);
p1.draw(); //画点:(1, 3)
Circle c1(1, 3, 5);
c1.draw(); //画圆:(1, 3, 5)
Rectandle r1(1, 3, 2, 4);
r1.draw(); //画矩形:((1, 3),(2, 4))
Ellipse e1(1, 1, 3, 6);
e1.draw(); //画椭圆:((1, 1), 3, 6)
return 0;
}
最后
以上就是壮观小鸽子为你收集整理的C++:继承、派生、多继承、补充:匈牙利命名法的全部内容,希望文章能够帮你解决C++:继承、派生、多继承、补充:匈牙利命名法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复