C++试水

  • 稍微学了一点c++,然后放弃了。。。
  • 这里是学习笔记
  • 学习平台在此

大门

使用旧方法创建工程

  • 使用Code::Blocks运行C++文件
    • 在新建路径内创建.cpp文件
    • 为了生成执行文件exe,于是使用控制台cmd,在该文件目录内:
      • g++ -o 执行文件名 代码文件名.cpp
  • 运行Code::Block

使用新方法创建工程

  • Code::Block->文件->新建->项目->ConsoleApplication

(一)你好世界

1
2
3
4
5
6
7
8
9
#include <iostream> //预处理器指令
using namespace std; // 引用"标准"命名空间

int main()
{
//cout即输出,<<即将字符串插入cout中,endl即\n并清空缓冲区
cout << "HelloWorld!" << endl;
return 0;
}

注释

1
2
3
4
// 单行注释
/*
多行注释
*/

(二)C++文件结构

1
2
3
//头文件(.h)的内容与源文件(.cpp)中的内容一致,都是 C++ 的源代码。
//但头文件不用被编译。
//将所有的函数声明全部放进一个头文件中,当源文件需要时,通过 "#include" 将头文件传入。

(三)数据类型

主要类型

数据类型 字符 位数 取值范围
整型 int 32 -2^31~2^31-1
短整型 short 16 -2^15_2^15-1
长整型 long long 64 -2^63~2^63-1
单精度浮点 float 32 -3.4e-38~3.4e+38
双精度浮点 double 64 1.7e-308~1.7e308
字符 char 8 -128~127

强制转型

1
2
float a = 10.5;
int b = (int)a;

常量与宏定义

1
2
#define HELLOWORLD "HelloWorld" //宏定义
const string HelloWorld = "HelloWorld"; //常量

(四)运算符

算数运算符

1
2
++ -- 递增 递减
+ - * / % 加 减 乘 除 余

其他运算符

1
2
3
4
5
= 赋值运算符
+= -= *= /= %= 复合运算符
> < >= <= == != 关系运算符
&& || ! 逻辑运算符
& | ~ ^ << >> 位运算符

(五)条件

如果、就、否则

1
2
3
4
5
6
7
8
9
10
11
12
if (true)
{
//TRUE
}
else if (false)
{
//FALSE
}
else
{
//???
}

当、就、否则

1
2
3
4
5
6
7
8
9
10
11
12
int i = 0;
switch(i)
{
case 0:
//...
break;
case 1:
//...
break;
default:
//...
}

(六)循环

while循环

1
2
3
4
5
6
int i = 1;
while (i <= 100)
{
//...
i++;
}

do-while循环

1
2
3
4
5
6
7
int i = 1;
do
{
//...
i++;
}
while (i <= 100)

for循环

1
2
3
4
5
6
7
8
9
10
11
12
for (int i = 0;i < 100;i++)
{
//...
if (i > 50)
{
break;
}
else
{
continue;
}
}

(七)数组

定义

1
2
3
4
5
int num1[] = {1, 2, 3, 4};
int num2[4];
int num3[4] = {1, 2, 3, 4};
int num3[4]{1, 2, 3, 4};
int num2[4]{};

动态赋值

1
2
3
4
5
6
int num[100]{};
for (int i = 0;i < 100;i++)
{
num[i] = i * 100;
cout << num[i] << endl;
}

数组长度

1
2
3
4
//sizeof()函数可以用来计算数据的比特大小
//整型与浮点型数值都是32位,每字节有8位,所以计算数值数组长度多半需要除4
int num[]{10, 20, 30, 40, 50};
cout << sizeof(num)/4 << endl;

排序

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int nums[]{10, 47, 8, 25, 20, 54, 61, 40};
int temp;
for (int i = 0;i < 8-1;i++)
{
for (int j = 0;j < 8-1-i;j++)
{
if (nums[j] > nums[j+1])
{
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
for (int i = 0;i < 8;i++)
{
cout << nums[i] << endl;
}

遍历

1
2
3
4
5
6
7
8
int nums[10];
int tick = 0;
for (auto i : nums)
{
i = tick;
cout << i << endl;
tick++;
}

(八)向量(动态数组)

定义

1
2
3
vector&lt;float&gt; vect1;
vector&lt;float&gt; vect2(5); //向量内有五个元素
vector&lt;float&gt; vect3(10, 1); //向量内有十个1

常用函数

1
2
3
4
5
6
7
8
9
10
11
vect1.clear(); //移除所有数据
cout << vect1.empty(); //判断是否为空
cout << vect1.size(); //返回数据个数
cout << vect1[1] << vect1.at(1); //返回索引后的数
vect1.erase(1); //删除指定数据
vect1.erase(1,3); //删除指定区间数据
cout << vect1.front(); //返回第一个元素
vect1.insert(1, 3.14); //插入元素
vect1.pop_back(); //删除最后一个元素
vect1.push_back(3.14); //末尾插入
vect1.resize(10); //重设大小

遍历

1
2
3
4
5
6
7
8
9
vector&lt;int&gt; vect;
for (int i = 0; i < 10; i++)
{
vect.push_back(i);
}
for (auto i : vect)
{
cout << i << endl;
}

(九)指针与引用

指针定义

1
2
3
4
5
int * p_num;
string * p_name;
//p_num1与*p_num2的区别
int* p_num1; //星偏向地址,则p_num1表地址
int *p_num2; //星偏向数值,则*p_num2表数值

指针赋值

1
2
int num = 10;
int* p_num = &num;

指针调用

1
2
3
4
5
6
int num = 10;
int* p_num = &num;
cout << *p_num << endl;
//
//&num表地址
//*p_num表数值

任意指针、空指针、野指针

1
2
3
4
int num = 10;
void* p_num = &num; //void* 即任意类型指针
int* ptr1 = nullptr; //空指针
int* ptr2; //野指针(一定要避免野指针)

指针与const

  • 当只有一个const时,如果const位于星的左侧,表示指针所指的数据是常量,无法通过该指针修改此数值,但指针本身是变量,可以指向其他数值
  • 当只有一个const时,如果const位于星的右侧,表示指针本身是常量,不能指向其他数值,但指针所指的数据是常量,可以通过该指针修改数值
1
2
3
int num = 10;
const int* p1_num = &num;//变量指针,常量数值
int* const p2_num = &num;//常量指针,变量数值

指针特殊情况

1
2
3
char ch = 'c'; //char型指针默认为字符串
char* p_ch = &ch;
cout << (void *)p_ch << *p_ch << endl;

动态分配内存

1
2
3
4
5
6
7
8
9
10
11
12
//指针用法:在运行阶段分配未命名的内存以存储值
//在此情况下,只能通过指针来访问内存
//通过“new”来分配内存
//1,在运行阶段为一个int值分配未命名的内存
int* p_int = new int;
//使用“*p_int”来访问数值
//2,释放由“new”分配的内存
delete p_int;
//不要使用delete释放不是new分配的内存
//数组稍有不同(delete[])
int* p_intArray1 = new int[4]{1, 2, 3, 4};
delete[] p_intArray1;

指针数组与数组指针

指针数组

  • 指针数组是由多个单个指针构成的数组
1
int* ptr[3]{nullptr, nullptr, nullptr};

数组名指针

  • 数组名都是指针,都是指向数组中第一个元素的常量指针
1
2
3
4
5
6
7
8
9
float num1[10];
float* p_num1;
p_num1 = num1;//p_num1即数组第一个元素指针
cout << p_num1 << &(num1[0]) << endl;//p_num1 = &(num1[0])
//通过数组名指针来获取数组里的其他值
float num2[3]{10, 20, 30};
float* p_num2 = num2;
cout << *(p_num2 + 1) << endl;//通过自增来渐进(输出20)
//上面使用数组名作为常量指针是合法的,因此,num2[2] = *(num + 2)

数组指针

  • 数组指针是一个数组的指针,可以是“指针数组”的指针
1
2
3
4
5
6
7
8
9
10
int array1[3]{10, 20, 30};
int (*ptr)[3] = &array1;//(*ptr)[3]作为一个指针指向了array1数组
cout << ptr << &(array1[0]) << endl;//相等
cout << (*ptr)[3] << &(array1[0]) << endl;//不等
//
//int (*ptr)[3]可以看成:
//倘若小括号没有,就成了int* ptr[3],星的优先级在于int了,就成了指针数组
//加上了小括号,星的优先级就成了ptr
//星在这里成了地址取值符而不是指针类型符,所以新声明的ptr必然是一个指针
//ptr单独拎出来还是数组第一个元素的地址

误区

1
2
3
4
5
int num[3] = {1, 2, 3};
// --------------------------------------
int* ptr1 = &num;//有误
int* ptr2 = num;//正确
int (*ptr3)[3] = &num;//正确

引用定义

  • 引用即为对象起另外一个名字
1
2
3
int num = 100;
int& refNum1 = num;//正确,num可以被refNum1调用
int& refNum2;//错误,引用必须初始化(不可直接引用常量,引用常量需要const一下)

(十)函数

定义

  • 函数需要定义原型定义实现体后才能使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int function_int(float);//函数原型声明
// ----------------------------------------
int main()//主函数
{
cout << function_int(0.58851) << endl;//函数调用
return 0;
}
// ----------------------------------------
//返回值类型 函数名 (参数列表)
int function_int(float f)//实现体
{
return (int)f;//函数体
//返回值不能为数组
}
  • 如果函数实现体在主函数之前声明,则可以不用声明函数原型(最好是要写)
  • 如果函数实现体在主函数之后声明,则必须在主函数前声明函数原型
  • 函数原型的存在目的就是为了在主函数前通知编译器函数的存在
1
2
3
4
5
6
7
8
9
10
int function_int(float f)//实现体
{
return (int)f;//函数体
}
// ----------------------------------------
int main()//主函数
{
cout << function_int(0.58851) << endl;//函数调用
return 0;
}

形参改变实参值值不变

  • 形参是实参的复制品,不会逆影响
1
2
3
4
5
6
7
8
9
10
11
12
void addSelf(int);
int main()
{
int m = 10;
cout << m << endl; //10
addSelf(m);
cout << m << endl; //10不变
}
void addSelf(int i)
{
i++;
}

使用引用形参来修改实参

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
27
28
29
30
31
void selfAdd1(int);//变量
void selfAdd2(int*);//指针
void selfAdd3(int&);//引用

int main()
{
int num1 = 10;
selfAdd1(num1);
cout << num1 << endl;//输出10,不变
//
int num2 = 10;
selfAdd2(&num2);
cout << num2 << endl;//输出10,不变
//
int num3 = 10;
selfAdd3(num3);
cout << num3 << endl;//输出11
}

void selfAdd1(int i)//变量
{
i++;
}
void selfAdd2(int* pi)//指针
{
pi++;
}
void selfAdd3(int& ri)//引用
{
ri++;
}

函数指针

  • 函数的地址是存储其机器语言代码的内存开始地址

函数指针声明

1
2
3
4
int sum(int, int);//函数原型声明
int (*pSum)(int, int);//函数指针声明
int* pSum(int, int);//错误方法
auto pSum = sum;//自动声明法(需要初始一个默认值)

使用示例

  • 函数可以通过函数指针的方法来作为其他函数的传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int sum(int, int);//函数原型声明
int (*pSum)(int, int);//函数指针声明
//将“int (*)(int, int)”作为一个函数指针参数传入函数中
void printRes(int (*)(int, int), int, int);//用于调用的void函数
// -----------------------------------------
int sum(int a, int b)//函数本体
{
return a + b;
}
void printRes(int (*pSum)(int, int), int a, int b)//调用函数的函数
{
int res = pSum(a, b);//pSum是函数指针
cout << res << endl;
}
// -----------------------------------------
int main()//主函数
{
pSum = sum;//指针指向函数
printRes(pSum, 10, 20);
}

内联函数

  • 编译器调用普通函数时,需要去查找函数地址去执行函数
  • 而调用内联函数时,只需要复制插入函数语句去执行即可
1
2
3
4
5
6
7
8
9
inline int add(int, int);
inline int add(int a, int b)
{
return a + b;
}
int main()
{
cout << add(1, 1) << endl;
}

函数与引用

引用返回值

  • 不要返回局部变量的引用,因为不干净
1
2
3
4
5
6
7
8
9
10
11
int& selfAdd(int);
int& selfAdd(int i)
{
int num = i + 1;
int& refNum = num;
return refNum;
}
int main()
{
cout << selfAdd(10) << endl;//不建议
}

函数左右值与引用

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
int& sum1(int&);
int& sum1(int& num)
{
num++;
return num;
}
// -----------------------------------
const int& sum2(int&);//加入const,不允许左右值互换
const int& sum2(int& num)
{
num++;
return num;
}
// -----------------------------------
int main()
{
int a = 10;
int& res1 = sum1(a);
sum(a) = 20;//引用函数允许放在左值
cout << res1 << endl;//输出20
//
int b = 10;
int& res2 = sum2(b);
sum(b) = 20;//直接报错
cout << res2 << endl;
}

函数默认参数

  • 可以在定义原型或者定义实现体时给函数的参数一个默认值
  • 但是不允许在两个地方同时赋值
  • 当有多个参数时,赋默认值的参数后的所有参数也必须得有默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void print(int = 10);//在定义原型时赋值
void print(int a)
{
cout << a << endl;
}
// -------------------------------
void print2(int, int, int, int);
void print2(int a, int b = 5, int c = 10, int d = 15)//多个默认值
{
cout << a + b + c + d << endl;
}
// -------------------------------
int main()
{
print();
print(20);
print2(10);
print2(10, 20, 30, 40);
}

函数重载

  • 重载指可以有多个同名的函数
  • 这些函数名称相同,但参数列表不同
  • 引用作为传参属于相同参数类型
  • 传参时不区分const标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void print(string);//print函数
void print(string str)
{
cout << str << endl;
}
void print(string, string);//print函数重载
void print(string str1, string str2)
{
cout << str1 << str2 << endl;
}
void print(string&);//print函数错误重载
void print(string& rstr)
{
cout << rstr << endl;
}
// -------------------------
int main()
{
string str = "HelloWorld!";
print(str);
print(str, str);
string& rstr = str;
print(rstr);//出错,引用作为传参属于相同参数类型
}

模板函数

  • 函数模板即建立一个通用函数
  • 函数定义时不指定具体的数据类型
  • 模板函数的意义是在函数内声明一个虚拟的数据类型
  • 编译器会自动推断模板函数虚拟数据类型的数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename T1, typename T2> void print(T1, T2, T1);//T1和T2是虚拟数据类型
//
template<typename T1, typename T2> void print(T1 a, T2 b, T1 c)
{
cout << a << b << c << endl;
}
// ------------------------------------
int main()
{
string str1 = "Hello";
int int1 = 10;
string str2 = "World!";
print(str1, int1, str2);
}

(十一)类

面向个体(面向对象)

  • “个体(对象)”在程序中是由数据和方法组成的封装体,并基于模拟现实来做对应
  • 类是概念的抽象,个体(对象)是类的实例

头文件.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include &lt;iostream&gt;
using namespace std;
//.h或.hpp文件里一般包含实现的内联函数
//在头文件中定义中,如果直接实现某成员函数,无论是否加inline,都是内联函数
class Student
{
private://私有访问修饰符
string _name;//成员变量
public://公有访问修饰符
int age = 10;
Student();//构造函数
~Student();//析构函数
void function1(int);//成员函数
inline void function2(string str)//内联函数
{
cout << str << endl;
}
protected://保护访问修饰符
};

源文件.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include &lt;iostream&gt;
#include "Student.h"//引用头文件
using namespace std;
//::作用域解析运算符
Student::Student()//构造函数
{

}
Student::~Student()//析构函数
{

}
void Student::function1(int num)//成员函数
{
cout << num << endl;
}

主函数调用

1
2
3
4
5
6
7
8
9
10
11
#include &lt;iostream&gt;
#include "Student.h"//引用头文件
using namespace std;
int main()
{
Student stu();//生成实例
stu.function1(12);
stu.function2("HelloWorld!");
cout << stu._name << endl;//报错,无法获取私有变量
cout << stu.age << endl;
}

访问修饰符

1
2
3
public://修饰的成员在任意地方都可以访问
private://修饰的成员只能在类中或者友元函数中才可访问
protected://修饰的成员能在类中、子类中或者友元函数中访问

类与结构体

  • 类的默认成员是私有的private
  • 结构体默认成员是公有的public
1
2
class Student{}
struct Teacher{}

面向个体之封装

  • 外部访问类中私有成员变量,使用封装函数
  • 当某个值确实需要私有化,且允许获取与设置时,使用自定义Get和Set函数进行封装
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
27
28
29
// 头文件 --------------------------------------
#include &lt;iostream&gt;
using namespace std;
class Teacher
{
private:
int _score;//私有变量推荐加上下划线或其他做区分
public:
Teacher();
~Teacher();
inline int getScore()//获取Get
{
return _score;
}
inline void setScore(int score)//设置Set
{
_score = score;
}
}
// 主函数 --------------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
int main()
{
Teacher teacher();
teacher.setScore(100);//设置
cout << teacher.getScore() << endl;//获取
}

构造函数

  • 构造函数以类名作为函数名,且构造函数没有返回值
  • 构造函数在实例被构造时会调用
  • 构造函数一般用于进行成员变量的初始化
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
27
28
29
30
31
32
33
34
35
36
37
// 头文件 -------------------------------
#include &lt;iostream&gt;
using namespace std;
class Player
{
private:
int _playerID = 1000;
public:
Player();//构造函数定义
inline void setPlayerID(int playerID)
{
_playerID = playerID;
}
}
// 源文件 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
Player::Player(){}//默认构造函数实现
Player::Player(int playerID)//带参构造函数实现
{
cout << "实例被构造" << endl;
_playerID = playerID;
}
Player::Player(int playerID, int adder)
: _playerID(playerID){}//初始化参数列表写法,使用“,”间隔
// 主函数 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
int main()
{
Player player1();
Player player2(1001);
Player* p_player3 = new player(1002);//使用指针来分配内存
p_player3->setPlayerID(1003);//对于指针而言,获取其原数据下的内容使用“->”
}

析构函数

  • 析构函数以“~”与类名作为函数名,且构造函数没有参数
  • 析构函数在实例过期时会调用
  • 析构函数一般用于进行成员变量的清理
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
27
28
29
30
31
32
33
34
// 头文件 -------------------------------
#include &lt;iostream&gt;
using namespace std;
class Player
{
private:
int _playerID = 1000;
public:
Player();
Player(int);
~Player();//析构函数定义
}
// 源文件 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
Player::Player(){}
Player::Player(int playerID)
{
_playerID = playerID;
}
Player::~Player(int playerID)//析构函数实现
{
delete _playerID;
}
// 主函数 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
int main()
{
Player player2(1001);//占用栈内存,直接释放
Player* p_player3 = new player(1002);//占用堆内存,手动释放
}

this指针

  • 包括构造与析构,类中每一个成员函数都有一个this指针
  • this指针指向当前调用个体(对象)
  • 可以通过this->(*this).来获取当前个体(对象)的成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 头文件 -------------------------------
#include &lt;iostream&gt;
using namespace std;
class Player
{
private:
int _playerID = 1000;
public:
Player();
~Player();
}
// 源文件 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
Player::Player(){}
Player::Player(int playerID)
{
this->_playerID = playerID;//this表示本地本物体
}
Player::~Player()
{
delete this->_playerID;
}

友元函数

  • 友元函数不是任何类的成员函数,却可以访问被允许类的私有数据
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
27
28
29
30
31
32
33
34
35
36
// 头文件 -------------------------------
#include &lt;iostream&gt;
using namespace std;
class Player
{
friend void SetPlayerID(Player player, int);//友元函数声明
private:
int _playerID = 1000;
int& r_playerID = _playerID;//形参只有指针或引用时才能被修改
public:
inline int getPlayerID()
{
return this->_playerID;
}
}
// 源文件 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
Player::Player(){}
Player::~Player(){}
// 主函数 -------------------------------
#include &lt;iostream&gt;
#include "头文件.h"
using namespace std;
void SetPlayerID(Player player, int playerID)//友元函数实现
{
player.r_playerID = playerID;
}
int main()
{
Player player();
cout << getPlayerID() << endl;//输出1000
SetPlayerID(player, 2000);
cout << getPlayerID() << endl;//输出2000
}

面向个体之继承

  • 继承即在一个已经存在的类的基础上再新建一个类,子类共用父类成员
  • 继承可以是公有继承私有继承以及受保护继承
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
27
28
29
30
31
32
33
34
// Human头文件 -------------------------------
class Human
{
private:
string _name;
int _age;
public:
Human();
void eat();
protected:
void sleep();//子类可访问
}
// Teacher头文件 -------------------------------
class Teacher : public Human
{
private:
string teacherWork _twork;
public:
Teacher();
void Teaching();
}
// Student头文件 -------------------------------
class Student : public Human
{
private:
string studentWork _swork;
public:
Student();
void Learning();
inline string getStudentName()
{
return _name;//调用父类成员
}
}

面向个体之多态

  • 多态性包括不同个体共用同一个函数调用,不同的个体在接收时会产生不同的行为

构成多态的条件

  • 必须存在类的继承关系
  • 必须有派生类重写基类的成员虚函数
  • 在外部使用基类的类指针来调用虚函数

多态多么变态

  • 当派生类重写基类中的成员函数时,将基类中被重写的函数设为虚函数virtual
  • 如果不添加virtual关键字,编译器就会根据当前个体类型调用其类型的成员方法
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include &lt;iostream&gt;
using namespace std;
// Parent头文件 -------------------------------
class Parent
{
public:
//该基类方法已被其他派生类重写,所以需要加virtual关键字
virtual void eat()//Parent基类的eat方法
{
cout << "Parent eat" << endl;
}
};
// Child头文件 -------------------------------
class Child : public Parent
{
public:
void eat() override//Child类的eat方法
{
cout << "Child eat" << endl;
}
};
// OtherChild头文件 -------------------------------
class OtherChild : public Parent
{
public:
void eat() override//OtherChild类的eat方法
{
cout << "OtherChild eat" << endl;
}
};
// 主函数 -------------------------------
int main()
{
Parent parent;
Child child;
OtherChild otherchild;
//
Parent* ptr_par;//创建基类类指针
ptr_par = &parent;
ptr_par->eat();//调用虚函数
//
ptr_par = &child;
ptr_par->eat();//调用虚函数
//
ptr_par = &otherchild;
ptr_par->eat();//调用虚函数
//
return 0;
}