<C++> 类(3):初始化列表 常函数和常量对象 虚函数与多态(包括纯虚函数)

一.初始化列表(初始化列表中必须有的两个内容)

1.类中const的成员变量:

①特点:不能修改 必须初始化 在构造函数后面加冒号 格式为:“:变量名(值)”

也就是说 常量必须在初始化列表中初始化

②执行顺序:构造函数先执行初始化列表 然后执行函数中的内容

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CPerson
 5 {
 6 public:
 7     const int a;
 8 public:
 9     CPerson():a(100)
10     {
11
12     }
13     void Show()
14     {
15         cout << a << endl;
16     }
17 };
18
19 int main()
20 {
21     CPerson ps;
22     ps.Show();
23
24     return 0;
25 }

2.组合关系的类中的构造函数有参数:

①例:包含了另一个类的对象要执行指定的构造函数

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CHead
 5 {
 6 public:
 7     CHead(int a)
 8     {
 9
10     }
11 };
12
13 class CPerson
14 {
15 public:
16     CHead head;
17 public:
18     CPerson():head(100)
19     {
20
21     }
22 };
23
24 int main()
25 {
26     CPerson ps;
27
28     return 0;
29 }

二.const常函数和常量对象

1.常函数:

①基本格式:void Show() const{ .. }

②特点:不能修改类中的成员变量

可以理解为 常函数参数括号中传的指针已经变成const CPerson* this这种指针了(拿CPerson为例)

也就是说 this已经变成const类型 这种指针的指向是不可以修改的 但是可以使用

2.常量对象:

①格式:const CPerson ps;

②特点:常量对象只能调用常函数 不能使用普通函数

可以理解为ps.Show(const CPerson*); 传入对象的地址类型不可改变

下面是一个常函数和常量函数的简单例子:

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CPerson
 5 {
 6 public:
 7     int m_nAge;
 8 public:
 9     CPerson()
10     {
11         m_nAge = 100;
12     }
13 public:
14     void Show() const
15     {
16         cout << m_nAge << endl;
17     }
18     void Show1()
19     {
20         m_nAge = 200;
21         cout << m_nAge << endl;
22     }
23 };
24
25 int main()
26 {
27     const CPerson ps;
28     CPerson ps1;
29     ps.Show();
30     ps1.Show1();
31
32     return 0;
33 }

三.虚函数与多态

1.引入:

①重载:同名 参数列表不同 调用父类的内容时要加作用域

②重写:同名 参数列表相同 全一致

③隐藏:是纵向重载 只要同名就好

2.父类的指针指向子类的对象:

①例如:CFather* pFather = new CSon;

②注意:这类指针只能看到父类的东西 但是子类的指针不能指向父类的对象

3.虚函数与多态:利用父类的复用性 充当接口类

①虚函数:virtual

作用:通过父类的指针调用子类的函数

注意:虚函数实现多态是基于重写来实现的 这使父类的指针具有多种形态

如果子类中重写了父类的函数 那么在虚函数列表中父类就被子类所覆盖

覆盖基于虚函数的重写 重写在子类中进行重写

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CWater
 5 {
 6 public:
 7     virtual void Show()
 8     {
 9         cout << "CWater::Show()" << endl;
10     }
11 };
12
13 class CMilk : public CWater
14 {
15 public:
16     void Show()
17     {
18         cout << "CMilk::Show()" << endl;
19     }
20 };
21
22 class CBeer : public CWater
23 {
24 public:
25     void Show()
26     {
27         cout << "CBeer::Show()" << endl;
28     }
29 };
30
31 void Bottle(CWater* cw)
32 {
33     cw -> Show();
34 }
35
36 int main()
37 {
38     CMilk cm;
39     CBeer cb;
40
41     Bottle(&cm);
42     Bottle(&cb);
43
44     return 0;
45 }

②多态:

用父类的指针指向子类 通过父类的指针调用子类成员函数

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CFather
 5 {
 6 public:
 7     virtual void AA()
 8     {
 9         cout << "CFather::AA()" << endl;
10     }
11     virtual void BB()
12     {
13         cout << "CFather::BB()" << endl;
14     }
15     void CC()
16     {
17         cout << "CFather::CC()" << endl;
18     }
19 };
20
21 class CSon : public CFather
22 {
23 public:
24     virtual void AA()
25     {
26         cout << "CSon::AA()" << endl;
27     }
28     void CC()
29     {
30         cout << "CSon::CC()" << endl;
31     }
32     void DD()
33     {
34         cout << "CSon::DD()" << endl;
35     }
36 };
37
38 int main()
39 {
40     CFather* cf1 = new CSon;
41
42     cf1 -> AA();
43     cf1 -> BB();
44     cf1 -> CC();
45
46     return 0;
47 }

4.(感觉上面整理的好乱)总结虚函数与多态:

①什么是多态:

父类的指针指向一个子类的对象 通过父类的指针调用实际子类的成员函数 这样才会使父类指针具有多种形态

②多态基于什么实现:

多态基于虚函数 虚函数基于重写

③多态的实现原理:虚函数列表和虚指针

在虚函数列表中 表中的每个元素都是一个函数指针 指向虚函数 或重写的虚函数

虚函数列表是在编译的时候就存在的 每一个类都有一个(对于普通的单继承来说)

虚指针就是记录使用哪一个列表的指针 在构造函数中被初始化 指向子类虚函数列表 这个指针是一个类成员

虚指针在创建对象的时候存在 在这个对象的首地址的前4个字节就是这个指针

那么我们就可以把调用一个虚函数理解为:通过虚指针拿到虚函数列表中的函数指针来实现调用的

④优缺点:

优点就是提高了复用性 扩展性

缺点就是安全性比较低 空间和效率问题也比较差

最后再放一个典型的例子加深理解:这段代码的输出足以说明这个问题

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CFather
 5 {
 6 public:
 7     void AA()
 8     {
 9         cout << "CFather::AA()" << endl;
10     }
11     virtual void BB()
12     {
13         cout << "CFather::BB()" << endl;
14     }
15 };
16
17 class CSon : public CFather
18 {
19 public:
20     void AA()
21     {
22         cout << "CSon::AA()" << endl;
23     }
24     virtual void BB()
25     {
26         cout << "CSon::BB()" << endl;
27     }
28 };
29
30 int main()
31 {
32     CFather* cf = new CSon;
33     cf -> AA();
34     cf -> BB();
35
36     CFather* cf1 = new CFather;
37     cf1 -> AA();
38     cf1 -> BB();
39
40     CSon* cs = new CSon;
41     cs -> AA();
42     cs -> BB();
43
44     return 0;
45 }

5.调用函数的区别:

①调用普通函数 调用哪一个函数 跟指针类型有关

②调用虚函数 看当前使用的虚函数列表是哪一个类的

③有作用域的话代表已经确定了 作用域的优先级别是最高的

6.纯虚函数:

①格式:virtual void 函数名() = 0;

②注意:

如果一个类包含纯虚函数 那这个类叫抽象类 抽象类是不可以定义对象的

在派生类中一定要实现纯虚函数 那么这个派生类 也叫具体类

如果一个类中的所有函数都是纯虚函数 那么这个类叫接口类

 1 #include<iostream>
 2 using namespace std;
 3
 4 class CPerson
 5 {
 6 public:
 7     virtual void Eat() = 0;
 8 };
 9
10 class CChina : public CPerson
11 {
12     virtual void Eat()
13     {
14         cout << "CChina::Eat()" << endl;
15     }
16 };
17
18 int main()
19 {
20     CPerson* cp = new CChina;
21     cp -> Eat();
22
23     return 0;
24 }

原文地址:https://www.cnblogs.com/Aaaaaalei0612/p/9195188.html

时间: 2024-10-27 09:53:38

<C++> 类(3):初始化列表 常函数和常量对象 虚函数与多态(包括纯虚函数)的相关文章

从Qt谈到C++(二):继承时的含参基类与初始化列表

提出疑问 当我们新建一个Qt的图形界面的工程时,我们可以看看它自动生成的框架代码,比如我们的主窗口名称为MainWindow,我们来看看mainwindow.cpp文件: MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { } 不同于一般的继承,这里的父类的括号里带有参数,我们通常都使用过不带参数,甚至不带括号的基类名称.这里的基类为什么带着参数呢? C++继承与构造函数

转:C++类构造函数初始化列表

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: class CExample { public:     int a;     float b;     //构造函数初始化列表     CExample(): a(0),b(8.8)     {}     //构造函数内部赋值     CExample()     {         a=0;         b=8.8;     } }; 上面的例子中两个构造函数的结果是一样

类的初始化列表的必要性

类的初始化列表的必要性: 1.当类中含有类类型成员时,必须用初始式进行初始化. 2.当类中含有const修饰的变量时,必须用初始式,因为在函数体里就是赋值了. 总结起来,可以初始化的情况有如下四个地方:1.在类的定义中进行的,只有const static  integral 修饰的变量.2.在类的构造函数初始化列表中, 包括const对象和Reference对象.3.在类的定义之外初始化的,包括static变量.因为它是属于类的唯一变量.4.普通的变量可以在构造函数的内部,通过赋值方式进行.当然

C++类构造函数初始化列表

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: class CExample {public:    int a;    float b;    //构造函数初始化列表    CExample(): a(0),b(8.8)    {}    //构造函数内部赋值    CExample()    {        a=0;        b=8.8;    }}; 上面的例子中两个构造函数的结果是一样的.上面的构造函数(使用初

C++类构造函数初始化列表(转)

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: { public:     int a;     float b;     //构造函数初始化列表     CExample(): a(0),b(8.8)     {}     //构造函数内部赋值     CExample()     {         a=0;         b=8.8;     } }; 上面的例子中两个构造函数的结果是一样的.上面的构造函数(使用初始化

自定义类的初始化列表

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class ConstRef { public:     ConstRef( int ii ); private:     int i;     const int ci;     int & ri; }; ConstRef::ConstRef( int ii ) {     i = ii;         /* ok */     ci = ii;     

C++类成员初始化列表的构造顺序

看下面代码, 输出结果是多少呢? class A{ public: A(int k) : j(k), i(j) { } void show() { cout << this->i << endl; cout << this->j << endl; } private: int i; int j; }; void test() { A a(3); a.show(); } int main(int argc, char const *argv[])

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++构造函数初始化列表与赋值

C++中类的初始化操作一般有四个部分组成: 1.构造函数初始化列表 2.构造函数体内赋值 3.类外部初始化 4.类声明时直接赋值 对于内部数据类型(char,int,float...),构造函数初始化列表和构造函数体内赋值,基本上没多大区别,效率差异也不大,但两者不能共存: class Student { public: Student(char *name, int age): //A初始化列表 m_name(name), m_age(age) { } Student(char *name,