(C++学习系列)第四集.

2023-11-11 04:40
文章标签 c++ 学习 系列 第四集

本文主要是介绍(C++学习系列)第四集.,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.传值,传址,传引用

1.1传值

首先需要明白的是传值的概念,它指的是在函数中直接把值进行传递,而不是以地址进行传递。代码如下

#include <iostream>
using namespace std;void change(int age1, int age2);int main()
{int age = 24;change(age, age + 1);//值传递,不是址传递,输出为25cout << "age = " << age << endl;//输出为24return 0;
}void change(int age1, int age2)//值传递,不是址传递
{age1 = age2;/*函数内部修改了值不会影响外部,除非是对地址的改变*/cout << "age1 = " << age1 << endl;
}

输出为:

这里可以发现age把自己的值直接传进之后,在函数里改变了值,却无法传递到函数的外部。

1.2传址

同样的代码,通过指针进行存储地址变量传递到函数中,记得地址是唯一的地址,通过解引用来访问并改变的值,即那个地址上的值进行了改变,所以是可以影响全局的。代码如下:

/*
函数内传值进行值改变无法影响函数外部,所以需要用指针来存址。
传地址需要用*对指针进行解引用
*/
#include <iostream>
using namespace std;void change(int *age1, int age2);int main()
{int age = 24;change(&age, age + 1);//第一个参数传输地址过去,址传递,输出为25cout << "age = " << age << endl;//输出为25return 0;
}void change(int *age1, int age2)//址传递
{*age1 = age2;/*改变了age1的地址,age1的值通过地址被改变,因为地址是唯一的,一定是改变的*/cout << "age1 = " << *age1 << endl;
}

地址上对应的值改变了。那必然会影响这个变量的值。因此结果如下:

下面一个经典的代码,交换两个数的大小,同样用到指针来进行储存地址,可以在把两者的交换传递到函数的外面。

#include <iostream>
using namespace std;void swap(int *a , int *b);/*C++里一定要有函数声明!*/int main()
{int a = 5;int b = 6;cout << "原来" << "a = " << a << '\n' << "b = " << b <<endl;swap(&a, &b);cout << "交换后" << "a = " << a << '\n' << "b = " << b <<endl;return 0;
}void swap(int *a , int *b)/*void型不应该有返回值*/
{*a = *a ^ *b;/*三次异或可以实现不需要第三个值两者交换*/*b = *a ^ *b;/*因为一个数异或同一个数两次,就等于原本的数。这里a^b^b就等于a赋值给b,同理*/*a = *a ^ *b;/*也可以这样理解,这里a^b以后变成了中间值,然后中间值^其他值就变成其他值了*/
}

这里采用了异或运算,就不需要第三个数来做媒介让两个数交换了

1.3传引用

这个主要是在声明和定义函数的时候,直接声明成地址进行传递(仅限于C++)

/*引用传递*/
/*
C++完善了地址这一个概念,如果事先就知道某个函数的参数只能接受一个地址,
能不能使用某种约定使得在调用该函数的时候不需要指针语法呢
其实他和传址的目的是一样的,都是把地址传递给函数,但语法不同更加容易使用了
*//*这里以swap改造*/
#include <iostream>
using namespace std;
/*函数声明中直接表明是整形地址,传递的时候直接传,就不需要传&a了*/
void swap(int &a , int &b);/*C++里一定要有函数声明!*/int main()
{int a = 5;int b = 6;cout << "原来" << "a = " << a << '\n' << "b = " << b <<endl;swap(a, b);/*直接传a和b*/cout << "交换后" << "a = " << a << '\n' << "b = " << b <<endl;return 0;
}void swap(int &a , int &b)/*这里直接用传入的a和b 运算,不是指针所以不需要解引用,输入a和b自动识别的是他们的地址*/
{a = a ^ b;/*三次异或可以实现不需要第三个值两者交换*/b = a ^ b;/*因为一个数异或同一个数两次,就等于原本的数。这里a^b^b就等于a赋值给b,同理*/a = a ^ b;/*也可以这样理解,这里a^b以后变成了中间值,然后中间值^其他值就变成其他值了*/
}

直接在函数内部就改变了地址,这里不需要用指针,在函数里会自动对其地址对应的值进行改变

输出同上

2.联合,枚举和类型别名

2.1联合

联合就是union,联合和结构有很多相似之处,联合也可以容纳多种不同类型的值,但是它每次只能存储这些值中的某一个。

例如:需要定义一个变量来存放密码,密码只有一个

union mima
{unsigned long birthday;unsigned short ssn;char* pet;
}
mima_1.birthday = 19881301
mima_1.pet = "chaozai" 如果定义了这个,会把原来的birthaday成员的值直接抛弃

举例:

#include <iostream>
using namespace std;union T
{const char *name;int a;char ch;
};int main()
{T birthday;birthday.name = "WaHaHa";cout << "birthday.name = " << birthday.name << endl;birthday.a = 3;//此时birthday.name中的内容被覆盖cout << "birthday.a = " << birthday.a << endl;return 0;
}

2.2枚举

枚举主要是为了创建一个可取值列表(会自动从0赋值),代码如下:

#include <iostream>
using namespace std;int main()
{//枚举中的值不是字符串,所以不用引号enum weekends{ Monday, Tuesday, Wednesday, Thursday, Friday};/*用0到n-1的整数关联起来,有点像集中定义的宏*/weekends today;today = Monday;cout << "today = " << today << endl;//输出0today = Wednesday;cout << "today = " << today << endl;//输出2return 0;
}

这里默认Monday为0,后面依次增加1,输出为:

2.3类型别名

typedef是用来定义类型别名的,比如typedef int* intPointer,那之后就可以用intPointer代替int*

3.介绍对象!

3.1创建一个类

在C++中,对象的本质就是新的数据类型,拥有无限的潜力。

类就是一个对象,我们从类开始,首先类是一个模型,当我们从这个类进行创建实例的时候,也就是对象本身,拥有扩展型和前瞻性。

类是一幅蓝图,决定对象是什么样的(具备什么属性和功能)
OOP(Object Oriented Programming面向对象编程)过程的第一步就是创建一个类,每个类都有一个名字:

class MyFirstClass
{
};

类是由变量(属性)和函数组成,对象将使用那些变量来存储信息,调用那些函数来完成操作。
所以人们常常会看到一些专门术语:类里面的变量成为属性,函数成为方法。当然本质没有改变。接下来我们来创建一个类(一个车)

#include <iostream>
using namespace std;
const unsigned int FULL_GAS = 80;
/*
声明了一辆车的简单属性
我们应该为类定义一些方法,其实也就是定义一些函数。创建个人函数也是两个步骤:
首先是创建函数的声明,再描述函数本身的实现过程
*/
class Car
{public:string color;string engine;float gas_tank;/*存放的油量*/unsigned int Wheel;/*现在我们的Car类有了一个名为fillTank的方法,只有一个输入参数,没有返回值*/void fill_tank(float liter);/*加油函数*/void setEngine(string end); /*设置引擎*/void setColor(string col); /*给车上色*/void setWheel(unsigned int whe);/*设置轮子*/void warning();/*警报*/int running(void);/*运行函数*/
};
/*::是作用域解析操作符,作用是告诉编译器这个方法存于何处*/void Car::warning()
{cout << "还剩油量:" << 100 * gas_tank / FULL_GAS << '%' << endl;
}
void Car::setEngine(string eng)
{engine = eng;
}
void Car::setWheel(unsigned int whe)
{Wheel = whe;
}
void Car::setColor(string col)
{color = col;
}
void Car::fill_tank(float liter)
{gas_tank += liter;
}
int Car::running()
{cout << "小车正在以120的速度跑着!!!" << endl;gas_tank--;cout << "当前油量:" << 100 * gas_tank / FULL_GAS << '%' << endl;return gas_tank;/*返回当前油量*/
}int main()
{char ch;//存储加油命令Car myCar;myCar.setColor("Black");myCar.setWheel(4);myCar.setEngine("V8");myCar.gas_tank = FULL_GAS;//加满油while ( myCar.running() ){if (myCar.running() < 10){myCar.warning();cout << "是否需要加油【Y/N】:" << endl;cin >> ch;switch (ch){case 'Y':case 'y':myCar.fill_tank(FULL_GAS);break;case 'N':case 'n':cout << "不加就不加" << endl;default:break;}}}return 0;}
/*面向對象編程技術,可以說是面向過程技術的替代平。他可以有自己的函數。
不允许类里面声明常量!但可以用静态常量,static const float FULL_GAS = 85;但一般没必要
类的话就不需要typedef来重命名了,因为不需要加class

这里我们在主函数里进行初始化(目前还没有用到构造器来初始化,后文会详细介绍)

3.2构造器和析构器

3.2.1构造器

构造器的主要功能是初始化函数,再用类创建实例的时候,第一个掉的就是构造器

构造器和通常方法的主要区别:
- 构造器的名字必须和它所在的类的名字一样
- 系统在创建某个类的实例的时候会第一时间自动调用这个类的构造器
- 构造器永远不会返回任何值!!!

创建构造器,需要把它的声明添加到类里,类名开头大写!

class Car
{
public:Car(void);………………
}Car::Car(void)    //不需要最前面加void,因为肯定不会有任何返回值
{color = "White";engine = "V8";wheel = 4;gas_tank = FULL_GAS;
}

在类的内部可以直接调用这些属性值,不需要用实例.   来进行取

代码案例如下:

#include <iostream>
using namespace std;
const unsigned int FULL_GAS = 80;class Car
{public:string color;string engine;float gas_tank;/*存放的油量*/unsigned int Wheel;Car(void);/*构造器*//*现在我们的Car类有了一个名为fillTank的方法,只有一个输入参数,没有返回值*/void fill_tank(float liter);/*加油函数*/void setEngine(string end); /*设置引擎*/void setColor(string col); /*给车上色*/void setWheel(unsigned int whe);/*设置轮子*/void warning();/*警报*/int running(void);/*运行函数*/
};
/*::是作用域解析操作符,作用是告诉编译器这个方法存于何处*/
Car::Car(void)/*构造函数用来初始化*/
{color = "White";engine = "V8";Wheel = 4;gas_tank = FULL_GAS;
}void Car::warning()
{cout << "还剩油量:" << 100 * gas_tank / FULL_GAS << '%' << endl;
}
void Car::setEngine(string eng)
{engine = eng;
}
void Car::setWheel(unsigned int whe)
{Wheel = whe;
}
void Car::setColor(string col)
{color = col;
}
void Car::fill_tank(float liter)
{gas_tank += liter;
}
int Car::running()
{char ch;cout << "小车正在以120的速度跑着!!!" << endl;gas_tank--;cout << "当前油量:" << 100 * gas_tank / FULL_GAS << '%' << endl;if (gas_tank < 10){warning();cout << "是否需要加油【Y/N】:" << endl;cin >> ch;switch (ch){case 'Y':case 'y':fill_tank(FULL_GAS);break;case 'N':case 'n':cout << "不加就不加" << endl;break;default:break;}}if(0 == gas_tank){cout << "没有油咯!" << endl;return 1;}return 0;}int main()
{Car myCar;myCar.gas_tank = FULL_GAS;//加满油while (!myCar.running() ){;}return 0;}

每个类至少有一个构造器!如果你没有在类里定义一个构造器,编译器就会替你创建一个副本构造器。

ps:

/*可以通过类来构造实例数组*/
Car mycar[10];
mycar[x].running;/*之类的*/

3.2.2析构器

在创建对象的时候,系统都会自动调用一个特殊的方法,即构造器。相应地,在销毁一个对象的时候,系统也应该会调用另一个特殊的方法来达到对应效果,即析构器。

一般来说,构造器用来完成事先的初始化和准备工作(申请分配内存),析构器用来完成事后所必须清理的工作(清理内存),析构器也不返回任何参数。

析构器也是跟类同名,但是前面需要加一个~

代码案例如下,功能是写入名言到文本中,这里析构器用来处理关闭文件

#include <iostream>
using namespace std;#include <string>//字符串操作
#include <fstream>//文件操作class StoreQuote
{
public:StoreQuote();//构造器~StoreQuote();//析构器string quote, speaker;//存储名言和作者ofstream OutFile;//写入文件类void inputQuote();void inputSpeaker();bool write();bool ifwrite();private:
};//构造器:事先完成初始化和准备工作
StoreQuote::StoreQuote()
{OutFile.open("test.txt", ios::app);//追加的方式写入文件
}//析构器:完成时候的清理工作
StoreQuote::~StoreQuote()
{OutFile.close();//关闭文件
}void StoreQuote::inputQuote()
{getline(cin, quote);//getline是string中的类,从输入中读取到quote中
}void StoreQuote::inputSpeaker()
{getline(cin, speaker);
}
bool StoreQuote::write()
{//判断文件是否打开if (OutFile.is_open())/*先写入字符串这里写入文件*/{OutFile << quote << "||来自“" << speaker <<  "”" << endl;/*换行了*/return true;}else{return false;}
}
bool StoreQuote::ifwrite()
{char ch;cout << "是否写入格言【Y/N】:";cin >> ch;cin.ignore(100, '\n');//忽略100个字符,除非遇到回车,这是为了清空输入流,即回车后把之前输入流的清空,这样才能接收新的输入switch (ch){case 'Y':case 'y':cout << "将格言写入文件" << endl;return true;break;case 'N':case 'n':cout << "格言不写入文件" << endl;return false;break;default:return false;/*break是一个好习惯,跳出switch,虽然这里有return了,但是还是加一下break把*/break;}
}int main()
{StoreQuote quote;while ( quote.ifwrite() ){cout << "请输入一句格言:" << endl;quote.inputQuote();/*如果没有ignore的话,第一个Y以及回车会被读入cin中,直接触发getline了,而不等你输出*/cout << "请输入作者的姓名:" << endl;quote.inputSpeaker();if (quote.write()){cout << "数据写入成功!" << endl;}else{cout << "数据写入失败!" << endl;cout << "输入任意字符退出" << endl;cin.get();return 1;}}cout << "输入任意字符退出" << endl;cin.get();return 0;
}

3.3this指针(暂时用不到)

当我们定义一个类的时候

class Human
{char fishc;Human(char fishc);   同名构造函数!
};

构造器里有一个名为fishc的参数,虽然他和Human类里面的属性同名,但是却不相干
所以需要让构造器明白到底谁才是参数,谁才是类的属性,这时候就需要用到this指针
this->fishc = fishc   左边是类的fishc属性,右边是构造器传入来的参数
存在二异性才用到this指针

3.4类的继承(重要)

基本思想:程序员可以对现有的代码进行进一步的扩展,并应用在新的程序中,比如写好了对象A,然后需要B去实现更复杂功能,且有A的特征,可以用B类去继承A。

假设有一只乌龟和一只猪,他们有共同特征:吃东西,睡觉,流口水,也有不同地方:乌龟会游泳,猪会爬树。两者都是动物,他们都是动物的子类

class SubClass : public SuperClass{…}  /*继承的写法*/
class Pig : public Animal{…}

 代码如下:

#include <iostream>
using namespace std;class Animal
{
public:string mouth;void eat();void sleep();void drool();
};class Pig : public Animal
{
public:void climb();
};class Turtle : public Animal
{
public:void swim();
};/*注意函数的写法哦*/
void Animal::eat()
{cout << "我正在吃" << endl;
}void Animal::sleep()
{cout << "我正在睡觉" << endl;
}void Animal::drool()
{cout << "我正在打呼噜" << endl;
}void Pig::climb()
{cout << "我正在上树" << endl;
}void Turtle::swim()
{cout << "我正在游泳" << endl;
}int main()
{/*初始化对象*/Pig pig;Turtle turtle;pig.eat();turtle.swim();return 0;
}

 3.5继承机制中的构造器和析构器

如基类有一个构造器,如Animal(),它将在创造Pig类型的对象时最先被调用,如果Pig类也有一个构造器,它将排在第二个被调用,因为基类必须要在子类之前初始化原则。

简易代码:(当构造器中有参数的时候写法),继承的子类的构造器就不许添加内容了,直接继承了基类的,但是赋值给子类

#include <iostream>
using namespace std;class Animal
{
public:Animal(string theName);string name;
};class Pig : public Animal
{
public:Pig(string theName);
};Animal::Animal(string theName)
{name = theName;
}
/*这样写是正确的!*/
Pig::Pig(string theName) : Animal(theName)/*这波是直接继承*/
{
}
/*当调用Pig构造器的时候(以theName作为输入参数),Animal()的构造器也将被调用(以theName作为输入参数)*/
/*这个赋值动作实际上是发生在Animal里的,比如调用Pig pig("小猪猪")*/

当在主函数里调用Pig pig进行实例化的时候,这里构造器有参数所以正确写法是 Pig pig("小猪猪"),就会把小猪猪赋值给pig.name

整体代码:

/*继承中的构造器和析构器:基类和子类*/
#include <iostream>
using namespace std;#include <string>
//基类(父类)动物
class Animal
{
public://属性string mouth;string name;Animal(string theName);//方法void eat();void sleep();void drool();
};
//子类1猪猪
class Pig : public Animal
{
public:void climb();Pig(string theName);//构造器
};
//子类2王八
class Turtle : public Animal
{
public:void swim();Turtle(string theName);
};
Animal::Animal(string theName)
{name = theName;
}
//基类中方法定义
void Animal::eat()
{cout << "I am eating!!!" << endl;
}
void Animal::sleep()
{cout << "I am sleeping!!!" << endl;
}
void Animal::drool()
{cout << "I am drooling!!!" << endl;
}
//子类中方法定义
void Pig::climb()
{cout << "我是一只漂亮的小母猪,我会上树,我正在爬树!" << endl;
}
Pig::Pig(string theName) : Animal(theName)//子类构造器定义
{
}
void Turtle::swim()
{cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里,哈哈!" << endl;
}Turtle::Turtle(string theName):Animal(theName)//子类构造器定义
{
}int main()
{/*使用Pig pig(参数)是用来调用构造器的!因为Pig pig初始化的时候就是调用了构造器,这里构造器需要参数,所以给个参数*/Pig pig("小猪猪");/*这里就直接传递了the Name给了构造器,是在Aniaml类的构造器里赋值的,但是继承过来了*/Turtle turtle("小甲鱼");cout << "这只猪的名字叫" << pig.name << endl;cout << "这只王八的名字叫" << turtle.name << endl;//子类自有的方法pig.eat();turtle.eat();//子类继承基类中的方法pig.climb();turtle.swim();return 0;
}

 

 

总体的调用顺序如下:

基类构造器—>子类构造器—>子类中的方法—>子类析构器—>基类析构器

为了观察这个顺序,我们可以写一个案例:(这里构造器没有参数,我们可以直接普通写法,不用像上面代码中继承基类的那种写法)

/*基类构造器—>子类构造器—>子类中的方法—>子类析构器—>基类析构器*/
#include <iostream>
using namespace std;class BaseClass
{
public:BaseClass();~BaseClass();void doSomething();
};class SubClass : public BaseClass
{
public:SubClass();~SubClass();
};BaseClass::BaseClass()
{cout << "进入基类构造器" << '\n';cout << "我在基类构造器里干了某些事情" << endl;
}BaseClass::~BaseClass()
{cout << "进入基类析构器" << '\n';cout << "我在基类析构器里也干了某些事情" << endl;    
}void BaseClass::doSomething()
{cout << "我干了某些事情" << endl;
}
/*这里不做继承,而是添加自己要干的东西*/
SubClass::SubClass()/*SubClass::SubClass():BaseClass()有参数的时候才这样写,括号里加参数*/
{cout << "进入子类构造器" << '\n';cout << "我在子类构造器里还干了某些事情" << endl;    
}SubClass::~SubClass()
{cout << "进入子类析构器" << '\n';cout << "我在子类析构器里也干了某些事情" << endl;        
}int main()
{SubClass subclass;subclass.doSomething();cout << "完事,收工" << endl;return 0;
}

3.6访问控制

新手需要注意的地方:基类和子类之间的关系应该自然且清晰,关于构造器设计要越简单越好,应该只用它来初始化各种有关的属性。

所谓访问控制,就是C++提供了一种用来保护类里面的方法和属性的手段
各种类允许访问的用户:

  • public:任何代码
  • protected:类本身和它的子类
  • private:只有这个类本身
class Animal
{
public:string name;Animal(string theName);void eat();void sleep();void drool();
};

因为是public意味着任何代码都可以访问它,所以上述很容易通过pig.name 等来修改名字。

注意这个类本身或者子类可以调用,其他代码不能调用的意思是,只能在类的内部进行运算,比如pig.name就相当于外面的代码进行访问了。如果是protect和private就是不允许的,但是可以构造器进行初始化,如下面案例:

#include <iostream>
using namespace std;#include <string>
//基类(父类)动物
class Animal
{
public://属性string mouth;Animal(string theName);//方法void eat();void sleep();void drool();
protected:string name;
};
//子类1猪猪
class Pig : public Animal
{
public:void climb();Pig(string theName);//构造器
};
//子类2王八
class Turtle : public Animal
{
public:void swim();Turtle(string theName);
};
Animal::Animal(string theName)
{name = theName;
}
//基类中方法定义
void Animal::eat()
{cout << "I am eating!!!" << endl;
}
void Animal::sleep()
{cout << "I am sleeping!!!" << endl;
}
void Animal::drool()
{cout << "I am drooling!!!" << endl;
}
//子类中方法定义
void Pig::climb()
{cout << "我是一只漂亮的小母猪,我会上树,我正在爬树!" << endl;
}
Pig::Pig(string theName) : Animal(theName)//子类构造器定义
{
}
void Turtle::swim()
{cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里,哈哈!" << endl;
}Turtle::Turtle(string theName):Animal(theName)//子类构造器定义
{
}int main()
{Pig pig("小猪猪");/*这是初始化的pig.name,只有这里才能用*/Turtle turtle("小甲鱼");/*如果在这里恶意修改了代码*//*pig.name = "笨蛋";                那么就不叫小猪猪了,叫笨蛋了*//*所以应该在前面写类的时候把name给保护起来*//*这里name就无法访问了*/cout << "这只猪的名字叫" << pig.name << endl;cout << "这只王八的名字叫" << turtle.name << endl;//子类自有的方法pig.eat();turtle.eat();//子类继承基类中的方法pig.climb();turtle.swim();return 0;
}

这里构造器初始化可以调用名字(protected),而pig.name就会报错

使用private的好处是,今后可以只修改某个类的内部实现,而不必重新修改整个程序,因为其他代码根本访问不到,不怕牵一发而动全身,把public,protect,private同一个属性的聚集在一块,从public开始写起,然后是protected,最后是private。

3.7覆盖和重载方法

在讲解这两个概念之前,我先提个问题:

想一想类的继承class Pig : public Animal{}是什么意思捏?

答: C++不仅允许你对在类里定义的方法和属性实施访问控制,还允许你对控制子类可以访问基类里的那些方法和属性。
public是在告诉编译器继承的方法和属性的访问级别不发生改变,即public可以被所有代码访问。protected只能由自己和子类访问。
private只能被自己访问。

如:把基类访问级别改为protected,如果原来是public,这将使得这个子类外部的代码,无法通过子类取访问基类中的public!(class Pig : protected Animal{}),private只有这个子类可以使用从基类继承过来的元素。但一般都只用public!(别搞得那么复杂)

3.7.1覆盖

所谓覆盖就是子类继承过来的函数,但是需要有它自己的实现方法,应该和基类有所区别,这个时候就需要用到覆盖。做法就是在类里面重新声明一下这个方法,再改写一下实现代码。

如下:

#include <iostream>
using namespace std;
#include <string>
//基类(父类)动物
class Animal
{
public://属性string mouth;Animal(string theName);//方法void eat();void sleep();void drool();
protected:string name;//这里定义name为保护的了,只有Animal本身以及其子类才可以修改
};
//子类1猪猪
class Pig : public Animal
{
public:void climb();void eat();//子类中覆盖基类中的方法Pig(string theName);//构造器
};
//子类2王八
class Turtle : public Animal
{
public:void swim();void eat();//子类中覆盖基类中的方法Turtle(string theName);
};
Animal::Animal(string theName)
{name = theName;
}
//基类中方法定义
void Animal::eat()
{cout << "I am eating!!!" << endl;
}
void Animal::sleep()
{cout << "I am sleeping!!!" << endl;
}
void Animal::drool()
{cout << "I am drooling!!!" << endl;
}
//子类中方法定义
void Pig::climb()
{cout << "我是一只漂亮的小母猪,我会上树,我正在爬树!" << endl;
}
/*覆盖了animal里面的eat*/
void Pig::eat()
{Animal::eat();//直接调用之前的基类函数cout << name << "正在吃草" << endl;
}Pig::Pig(string theName) : Animal(theName)//子类构造器定义
{
}
void Turtle::swim()
{cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里,哈哈!" << endl;
}/*覆盖了animal里面的eat*/
void Turtle::eat()
{Animal::eat();//直接调用之前的基类函数cout << name << "我正在吃鱼" << endl;
}Turtle::Turtle(string theName):Animal(theName)//子类构造器定义
{
}int main()
{Pig pig("小猪猪");Turtle turtle("小甲鱼");//子类自有的方法pig.eat();turtle.eat();//子类继承基类中的方法pig.climb();turtle.swim();system("pause");return 0;
}

在子类定义的时候重新声明一次函数,后面还需要重新写一下函数内容(子类::函数名),这样就可以覆盖基类函数的内容。当然只在这个子类中覆盖,其他的子类还是可以调用基类函数的。

3.7.2重载

/*重载方法*/
#include <iostream>
using namespace std;#include <string>
//基类(父类)动物
class Animal
{
public://属性string mouth;Animal(string theName);//方法void eat();void eat(int eatcount);void sleep();void drool();
protected:string name;//这里定义name为保护的了,只有Animal本身以及其子类才可以修改
};
//子类1猪猪
class Pig : public Animal
{
public:void climb();Pig(string theName);//构造器
};
//子类2王八
class Turtle : public Animal
{
public:void swim();Turtle(string theName);
};
Animal::Animal(string theName)
{name = theName;
}
//基类中方法定义
void Animal::eat(int eatcount)
{cout << "我吃了" << eatcount << "馄饨" << endl;
}
void Animal::eat()
{cout << "I am eating!!!" << endl;
}
void Animal::sleep()
{cout << "I am sleeping!!!" << endl;
}
void Animal::drool()
{cout << "I am drooling!!!" << endl;
}
//子类中方法定义
void Pig::climb()
{cout << "我是一只漂亮的小母猪,我会上树,我正在爬树!" << endl;
}Pig::Pig(string theName) : Animal(theName)//子类构造器定义
{
}
void Turtle::swim()
{cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里,哈哈!" << endl;
}Turtle::Turtle(string theName):Animal(theName)//子类构造器定义
{
}int main()
{Pig pig("小猪猪");Turtle turtle("小甲鱼");//子类继承基类中的方法pig.eat();turtle.eat();pig.eat(5);//重载turtle.eat(10);//子类自有的方法pig.climb();turtle.swim();system("pause");return 0;
}
/*一定要注意啊重载还是覆盖!*/

注意:继承后不可以重载!继承后的子类如果声明了基类中的函数(声明了函数名,后面的参数不管一不一样)都是覆盖,覆盖了之后,原本的基类的这个函数在子类中就无法被调用了,这里指的无法被调用是说主函数中,如果pig.eat(),那就是pig里的,而不是基类里的。当然子类的函数可以调用比如说在void Pig::eat()函数中调用Animal::eat()。

总的来说:覆盖优先

代码如下:

/*对继承下来的进行重载*/
#include <iostream>
using namespace std;
#include <string>
//基类(父类)动物
class Animal
{
public://属性string mouth;Animal(string theName);//方法void eat();void sleep();void drool();
protected:string name;//这里定义name为保护的了,只有Animal本身以及其子类才可以修改
};
//子类1猪猪
class Pig : public Animal
{
public:void climb();Pig(string theName);//构造器void eat(int eatcount);//在继承基类中进行重载
};
//子类2王八
class Turtle : public Animal
{
public:void swim();Turtle(string theName);
};
Animal::Animal(string theName)
{name = theName;
}//基类中方法定义
void Animal::eat()
{cout << "I am eating!!!" << endl;
}
void Animal::sleep()
{cout << "I am sleeping!!!" << endl;
}
void Animal::drool()
{cout << "I am drooling!!!" << endl;
}//子类中方法定义
void Pig::climb()
{cout << "我是一只漂亮的小母猪,我会上树,我正在爬树!" << endl;
}void Pig::eat(int eatcount)
{cout << "我吃了" << eatcount << "馄饨" << endl;
}
Pig::Pig(string theName) : Animal(theName)//子类构造器定义
{
}
void Turtle::swim()
{cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里,哈哈!" << endl;
}Turtle::Turtle(string theName):Animal(theName)//子类构造器定义
{
}int main()
{Pig pig("小猪猪");Turtle turtle("小甲鱼");//子类继承基类中的方法pig.eat();/*这里出错了,因为已经被有参数的覆盖了,继承后不能重载!!!,eat都没了*/turtle.eat();pig.eat(5);//重载//子类自有的方法pig.climb();turtle.swim();system("pause");return 0;
}

这篇关于(C++学习系列)第四集.的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/387603

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名