我是靠谱客的博主 壮观小鸽子,最近开发中收集的这篇文章主要介绍C++:继承、派生、多继承、补充:匈牙利命名法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、继承/派生
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++:继承、派生、多继承、补充:匈牙利命名法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部