虚函数:类的成员函数,前面有关键词virtual
作用:在公有继承层次中的一个或多个派生类中对虚函数进行重定义。当派生的对象使用它基类的指针(或引用)调用虚函数时,将调用该对象的成员函数。
一旦为虚,永远为虚;
虚函数即可以被继承,也可以被重定义。
覆盖:修改基类函数定义,被virtual修饰,相同签名
隐藏:屏蔽基类函数定义,同名,参数列表可同可异,无virtual修饰
对象指针(引用)被向上转型到基类指针?派生类对象赋值到基类对象?
多态:一个标识符指代不同类型的数据,有具体的数据类型决定该标识符的语义
函数重载,方法覆盖,泛型模板支持
静态类型:沿继承类向上,找到最近的成员函数(静态联编)
重写一个相同函数名,相同参数的函数,会覆盖或隐藏之前继承而来的函数。
基类指针指向派生类对象,这对于实现多态性的行为是至关重要的。
#include <iostream>
class Fruit{
public:
void say() {
printf("I'm a fruit!\n");
}
};
class Apple : public Fruit {
public:
void say() {
printf("I'm an apple!\n");
}
};
class Banana : public Fruit {
public:
void say() {
printf("I'm a banana!\n");
}
};
class Cherry : public Fruit {
public:
void say() {
printf("I'm a cherry!\n");
}
};
int main(){
Apple a;
Fruit *fPtr = &a;
fPtr->say();
}
//输出“i am a fruit."
C++默认重写为隐藏
动态类型:调用指针所指向的对象的成员函数
如何处理抽象概念:1.保护构造函数;2.基类虚函数抛出异常
纯虚函数:virtual void eat() =0;
抽象类:定义或继承了至少一个纯虚函数的类(不能被实例化,子类是抽象类)
能仅能作为基类指针或引用,不可能存在抽象对象
c++模拟接口:不包含自己的数据,成员函数都是虚基类,可以包含静态成员,有虚析构,不要构造函数,保证唯一,可以无歧义的被多继承,在子类中实现所有的虚函数,否则产生错误。
任何包含虚函数的基类的析构函数必须为公开且虚,祸受保护且非虚。否则很容易导致内存泄漏。
rtti:运行时类型识别(typeid和dynamic_cast)
typeid:
头文件:typeinfo
参数:类型,表达式(动态:多态;静态:非多态)
dynamic_cast:
向上向下侧向,dynamic_cast<新类型>(表达式)
失败:指针:空指针;引用:抛出异常。
final说明符:
指定某个虚函数不能再子类中被覆盖,或者某个类不能被子类继承。
12-H1
Description
设计一个基类base
,包含姓名(name
)和年龄(age
)私有数据成员以及相关的成员函数(getter、setter);
由base
派生出领导类leader
,包含职务(job
)和部门(department
)私有数据成员以及相关的成员函数(getter、setter);
由base
派生出工程师类engineer
,包含职称(profile
)和专业(major
)私有成员以及相关的成员函数(getter、setter);
由leader
和engineer
类派生出主任工程类chairman
,主函数将采用一些数据测试这个类。
#include<iostream>
#include<string.h>
#include<cstring>
#include <fstream>
using namespace std;
class base {
char name[20];
int age;
public:
base() {}
void setname(char arr[]) {
strcpy(name, arr);
};
void setage(int age) {
this->age = age;
};
char* getname() {
return name;
};
int getage() {
return age;
};
};
class leader: virtual public base {
private:
char job[20];
char department[20];
public:
void setjob(char arr[]) {
strcpy(job, arr);
};
void setdep(char arr[]) {
strcpy(department, arr);
};
char * getjob() {
return job;
}
char * getdep() {
return department;
}
};
class engineer: virtual public base {
private:
char major[20];
char profession[20];
public:
void setmajor(char arr[]) {
strcpy(major, arr);
};
void setprof(char arr[]) {
strcpy(profession, arr);
};
char * getmajor() {
return major;
}
char * getprof() {
return profession;
}
};
class chairman: public leader, public engineer {
};
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int n;
cin>>n;
for (int i=0;i<n;i++)
{
char name[20],job[20],dep[20],major[20],prof[20];
int age;
cin>>name>>age>>job>>dep>>major>>prof;
chairman man;
man.setname(name);
man.setage(age);
man.setjob(job);
man.setdep(dep);
man.setmajor(major);
man.setprof(prof);
cout<<"name: "<<man.getname()<<" age: "<<man.getage()<<" dep: "<<man.getdep()
<<" job: "<<man.getjob()<<endl;
cout<<"prof: "<<man.getprof()<<" major: "<<man.getmajor()<<endl;
}
return 0;
}
菱形继承需要使用虚基类,字符串拷贝要用到虚继承。
12-H2
假设这个世界上有3大种族:野兽、人类和兽人。所有这些种族都继承于生物类。而且由于兽人可以看作是野兽与人类的结合体,所以兽人类应当同时继承于野兽类和人类。
#include <iostream>
using namespace std;
class Creature {
public:
Creature(const int& hands, const int& legs): _hands(hands), _legs(legs) {
cout << "A Creature has been created!" << endl;
cout << "It has " << _hands << " hand(s)!" << endl;
cout << "It has " << _legs << " leg(s)!" << endl;
}
virtual ~Creature() {
cout << "Creature object exiled!" << endl;
}
int GetHands() const {
return _hands;
}
int GetLegs() const {
return _legs;
}
private:
int _hands;
int _legs;
};
class Beast : virtual public Creature {
public:
Beast(const int& hands, const int& legs, const string& name): Creature(hands, legs), _name(name) {
cout << "Its beast name is " << _name << endl;
}
virtual ~Beast() {
cout << "Beast object exiled!" << endl;
}
string GetName() const {
return _name;
}
private:
string _name;
};
class Human: virtual public Creature {
public:
Human(const int& hands, const int& legs, const string& name): Creature(hands, legs), _name(name) {
cout << "Its human name is " << _name << endl;
}
virtual ~Human() {
cout << "Human object exiled!" << endl;
}
string GetName() const {
return _name;
}
private:
string _name;
};
class Orc: virtual public Beast, virtual public Human {
public:
Orc(const int& hands, const int& legs, const string& beast_name, const string& human_name): Creature(hands, legs), Beast(hands, legs, beast_name), Human(hands, legs, human_name) {
}
~Orc() {
cout << "Orc object exiled!" << endl;
}
string GetBeastName() const {
return Beast::GetName();
}
string GetHumanName() const {
return Human::GetName();
}
};
#include <iostream>
using namespace std;
class Creature {
public:
Creature(const int& hands, const int& legs): _hands(hands), _legs(legs) {
cout << "A Creature has been created!" << endl;
cout << "It has " << _hands << " hand(s)!" << endl;
cout << "It has " << _legs << " leg(s)!" << endl;
}
virtual ~Creature() {
cout << "Creature object exiled!" << endl;
}
int GetHands() const {
return _hands;
}
int GetLegs() const {
return _legs;
}
private:
int _hands;
int _legs;
};
class Beast : virtual public Creature {
public:
Beast(const int& hands, const int& legs, const string& name): Creature(hands, legs), _name(name) {
cout << "Its beast name is " << _name << endl;
}
virtual ~Beast() {
cout << "Beast object exiled!" << endl;
}
string GetName() const {
return _name;
}
private:
string _name;
};
class Human: virtual public Creature {
public:
Human(const int& hands, const int& legs, const string& name): Creature(hands, legs), _name(name) {
cout << "Its human name is " << _name << endl;
}
virtual ~Human() {
cout << "Human object exiled!" << endl;
}
string GetName() const {
return _name;
}
private:
string _name;
};
class Orc: virtual public Beast, virtual public Human {
public:
Orc(const int& hands, const int& legs, const string& beast_name, const string& human_name): Creature(hands, legs), Beast(hands, legs, beast_name), Human(hands, legs, human_name) {
}
~Orc() {
cout << "Orc object exiled!" << endl;
}
string GetBeastName() const {
return Beast::GetName();
}
string GetHumanName() const {
return Human::GetName();
}
};
int main() {
cout << "*************** Testing class Creature ***************" << endl;
Creature *unknown = new Creature(2, 2);
cout << "Calling GetHands(): " << unknown->GetHands() << endl;
cout << "Calling GetLegs(): " << unknown->GetLegs() << endl;
delete unknown;
cout << endl;
cout << "*************** Testing class Beast ***************" << endl;
Creature *beast1 = new Beast(2, 4, "SpiderKing");
cout << "Calling GetHands(): " << beast1->GetHands() << endl;
cout << "Calling GetLegs(): " << beast1->GetLegs() << endl;
delete beast1;
cout << endl;
Beast *beast2 = new Beast(2, 3, "TripleLegCat");
cout << "Calling GetHands(): " << beast2->GetHands() << endl;
cout << "Calling GetLegs(): " << beast2->GetLegs() << endl;
cout << "Calling GetName(): " << beast2->GetName() << endl;
delete beast2;
cout << endl;
cout << "*************** Testing class Human ***************" << endl;
Creature *human1 = new Human(2, 2, "Talion");
cout << "Calling GetHands(): " << human1->GetHands() << endl;
cout << "Calling GetLegs(): " << human1->GetLegs() << endl;
delete human1;
cout << endl;
Human *human2 = new Human(2, 2, "Altariel");
cout << "Calling GetHands(): " << human2->GetHands() << endl;
cout << "Calling GetLegs(): " << human2->GetLegs() << endl;
cout << "Calling GetName(): " << human2->GetName() << endl;
delete human2;
cout << endl;
cout << "*************** Testing class Orc ***************" << endl;
Creature *orc1 = new Orc(2, 4, "Herkarim", "Centaur");
cout << "Calling GetHands(): " << orc1->GetHands() << endl;
cout << "Calling GetLegs(): " << orc1->GetLegs() << endl;
delete orc1;
cout << endl;
Beast *orc2 = new Orc(2, 2, "OgreMagi", "BlueFat");
cout << "Calling GetHands(): " << orc2->GetHands() << endl;
cout << "Calling GetLegs(): " << orc2->GetLegs() << endl;
cout << "Calling GetName(): " << orc2->GetName() << endl;
delete orc2;
cout << endl;
Human *orc3 = new Orc(4, 2, "Miranda", "NotreDame");
cout << "Calling GetHands(): " << orc3->GetHands() << endl;
cout << "Calling GetLegs(): " << orc3->GetLegs() << endl;
cout << "Calling GetName(): " << orc3->GetName() << endl;
delete orc3;
cout << endl;
Orc *orc4 = new Orc(2, 0, "Cassiopea", "SnakeWoman");
cout << "Calling GetHands(): " << orc4->GetHands() << endl;
cout << "Calling GetLegs(): " << orc4->GetLegs() << endl;
cout << "Calling GetBeastName(): " << orc4->GetBeastName() << endl;
cout << "Calling GetHumanName(): " << orc4->GetHumanName() << endl;
delete orc4;
cout << endl;
return 0;
}
虚析构?有什么用?什么时候需要?
12-H3
#include <iostream>
using namespace std;
class Number {
private:
int num;
public:
Number(int n) {
num = n;
}
Number& add(int n) {
num += n;
return *this;
}
Number& sub(int n) {
num -= n;
return *this;
}
void print() {
cout << num << endl;
}
int main(){
Number n(1);
n.add(2).sub(3).add(4);
n.print();
Number n1(-1);
n1.sub(12).sub(3).sub(4);
n1.print();
Number n2(0);
n2.add(-13).add(0).add(0);
n2.add(13);
n2.print();
return 0;
}
返回引用以实现连续调用同一个对象,返回值会生成拷贝,造成答案错误。
13-K1
假设有一个基类Animal,它有一个虚函数speak(),然后有两个派生类Dog和Cat,它们都重载了speak()函数。请写出一个程序,使得在主函数中定义一个Animal指针并指向Dog或Cat的对象时,调用speak()函数能够正确地输出Dog或Cat的声音。
#include <iostream>
using namespace std;
class Animal {
public:
virtual void speak() {
cout << "Animal speaks" << endl;
}
};
class Dog : public Animal {
public:
void speak() {
cout << "Dog barks" << endl;
}
};
class Cat : public Animal {
public:
void speak() {
cout << "Cat meows" << endl;
}
};
int main() {
Animal a;
Animal *p;
Dog d;
Cat c;
p=&a;
p->speak();
p = &d;
p->speak();
p = &c;
p->speak();
return 0;
}
虚函数会在后面的函数重载中被重载,实现调用子类的元素。
13-K2
假设我们有一个基类Vehicle,它有一个纯虚函数run()。然后有两个派生类Car和Truck,以及一个继承自Car和Truck的类Bus。请写出它们的实现代码,使得在主函数中定义一个Vehicle指针指向Car、Truck或Bus的对象时,可以调用run()函数并能正确输出汽车的行驶距离。
#include <iostream>
using namespace std;
class Vehicle {
public:
virtual void run() = 0;
};
class Car : virtual public Vehicle {
public:
Car(float d = 0) {
distance = d;
}
void run() {
cout << "Car running " << distance << " kilometers." << endl;
}
private:
float distance;
};
class Truck : virtual public Vehicle {
public:
Truck(float d = 0) {
distance = d;
}
void run() {
cout << "Truck running " << distance << " kilometers." << endl;
}
private:
float distance;
};
class Bus : public Car, public Truck {
public:
Bus(float d = 0) : Car(d), Truck(d) {
distance = d;
}
void run() {
cout << "Bus running " << distance << " kilometers." << endl;
}
private:
float distance;
};
int main() {
Vehicle *vehicle;
Car car(100);
Truck truck(200);
Bus bus(300);
vehicle = &car;
vehicle->run();
vehicle = &truck;
vehicle->run();
vehicle = &bus;
vehicle->run();
return 0;
}
纯虚函数在基类定义的时候不需要具体实现,但是要在子类中写出实现。
13-H1
写出3组Plus
函数的声明与实现,类型分别为int
,double
,string
。
#include <iostream>
#include <cstring>
int myplus(int a,int b)
{
return a+b;
}
double myplus(double c,double d)
{
return c+d;
}
std::string myplus( std::string str1, std::string str2)
{
return str1+str2;
}
int main() {
int n;
int a,b;
double c,d;
std::string str1,str2;
std::cin>>n;
while(n--)
{
std::cin>>a >> b ;
std::cin>>c >> d;
std::cin >> str1 >>str2;
std::cout <<myplus(a, b) << endl;
std::cout <<myplus(d, c) << endl;
std::cout <<myplus(str1, str2) << endl;
}
return 0;
Please add std::
before using the string type.
13-H2
using namespace std;
class MyDate {
private:
int year;
int month;
int day;
};
class Person {
private:
string name;
string address;
string phoneNumber;
string email;
public:
virtual string toString() const {
return "Person";
}
};
class Student:public Person {
private:
enum class_status {frssman,sophomore,junior,senior};
class_status status;
public:
string toString() const {
return "Student";
}
};
class Employee:public Person {
private:
string office;
int salary;
MyDate dateHired;
public:
string toString() const {
return "Employee";
}
};
class Faculty:public Employee {
private:
string officeHours;
int rank;
public:
string toString() const {
return "Faculty";
}
};
class Staff:public Employee {
private:
string title;
public:
string toString() const {
return "Staff";
}
};
class class_status {};
void f1(Person p)
{
cout << p.toString() << endl;
}
void f2(Employee e)
{
cout << e.toString() << endl;
}
void f(const Person &p)
{
cout << p.toString() << endl;
}
int main()
{
Person person;
Student student;
Employee employee;
Faculty faculty;
Staff staff;
f1(person);
f1(student);
f1(employee);
f1(faculty);
f1(staff);
f2(employee);
f2(faculty);
f2(staff);
f(person);
f(student);
f(employee);
f(faculty);
f(staff);
return 0;
}
?纯虚函数和虚函数的区别?
13-H3
Feng Gor是著名的房地产商,她每天的主要工作就是——数钱。这并不是一件容易的事,Feng Gor主要投资两类地,一类是圆的,一类是方的。现在你作为她的会计,需要帮她数钱。
Circle
类和Square
类继承于Land
类,分别代表圆的地和方的地,其投资收入根据单位面积价格(price
)和地的面积(根据边长或半径算出)决定。
Accountant
类用于计算Feng Gor开发各种房地产带来的收入。其中DevelopEstate
函数接收一个Land
的指针,计算并累加开发这块房地产带来的收入(这里用到了动态绑定哦)。CheckMoney
函数返回当前的收入。
#include <iostream>
#include <cmath>
using namespace std;
class Land {
public:
Land():price_(0) {}
explicit Land(int price) {
price_ = price;
}
virtual double CalMoney() = 0;
protected:
int price_;
};
class Square: public Land {
public:
Square(double len, int price) {
len_ = len;
price_ = price;
}
double CalMoney() {
return pow(len_, 2) * price_;
}
private:
double len_;
};
class Circle:public Land {
public:
Circle(double radius, int price) {
radius_ = radius;
price_ = price;
}
double CalMoney() {
return acos(-1) * pow(radius_, 2) * price_;
}
private:
double radius_;
};
class Accountant {
public:
Accountant():money_(0) {}
void DevelopEstate(Land* land) {
money_ += land -> CalMoney();
}
double CheckMoney() {
return money_;
}
private:
double money_;
};
int main(int argc, const char *argv[]) {
Accountant py;
Circle *a = new Circle(179, 2938); //第一个参数为半径,第二个参数为单位面积价格
Square *b = new Square(90.8, 1290); //第一个参数为边长,第二个参数为单位面积价格
py.DevelopEstate(a);
cout << setprecision(10) << py.CheckMoney() << endl;
py.DevelopEstate(b);
cout << setprecision(10) << py.CheckMoney() << endl;
return 0;
}
如果一个函数或者一个变量在诸多子类中都要用到,不妨先在基类中声明或者定义为虚函数,这样把事情多分担给编译器,指分辨出目标变量具体属于哪一个类(?怎么做到的?)。
14-K1
编写一个求数组中的最大元素的函数模板 T Max(T *a, int n)
,其中,a
为数组,T
为数组类型,n
为数组的长度。
数组的类型可以是int
型、double
型和string
类型。数组中元素个数3≤n≤20
。
#include<iostream>
#include<string>
using namespace std;
template<typename T>
T Max(T *a, int n)
{
int j = 0;
for (int i = 1; i < n; i++)
{
if (a[i] > a[j])
j = i;
}
return a[j];
}
int main()
{
int n1, n2, n3;
int a[20];
double b[20];
string c[20];
cin >> n1;
for (int i = 0; i < n1; i++)
cin >> a[i];
cin >> n2;
for (int i = 0; i < n2; i++)
cin >> b[i];
cin >> n3;
for (int i = 0; i < n3; i++)
cin >> c[i];
cout << Max(a, n1) << endl;
cout << Max(b, n2) << endl;
cout << Max(c, n3) << endl;
return 0;
}
学习模板的编写。
14-K2
定义一个抽象计算器类AbstractCalculator
,它有一个虚函数成员getResult()
,返回计算器计算结果(默认返回0)。
由AbstractCalculator
类派生出一下几类:加法器类Add
,减法器类Sub
,乘法器类Mul
,需要重新实现getResult()
函数,使之返回正确的运算结果。
#include <iostream>
//抽象计算机
class AbstractCalculator
{
public:
virtual double getResult()
{
return 0;
}
double m_num1;
double m_num2;
};
//加法
class Add : public AbstractCalculator
{
public:
double getResult()
{
return m_num1+m_num2;
}
};
//减法
class Sub : public AbstractCalculator
{
public:
double getResult()
{
return m_num1-m_num2;
}
};
//乘法
class Mul : public AbstractCalculator
{
public:
double getResult()
{
return m_num1 * m_num2;
}
};
int main()
{
//加法
AbstractCalculator *abc = new Add;
cin >> abc->m_num1 >> abc->m_num2;
cout << abc->m_num1 << " + " << abc->m_num2 << " = " << abc->getResult() << endl;
delete abc;
//减法
abc = new Sub;
cin >> abc->m_num1 >> abc->m_num2;
cout << abc->m_num1 << " - " << abc->m_num2 << " = " << abc->getResult() << endl;
delete abc;
//乘法
abc = new Mul;
cin >> abc->m_num1 >> abc->m_num2;
cout << abc->m_num1 << " * " << abc->m_num2 << " = " << abc->getResult() << endl;
delete abc;
return 0;
}
14-H1
编写程序,定义抽象类Container
,有double
型保护数据成员radius
和计算表面积的函数calSurfaceArea()
以及计算体积的函数calVolume()
。
#include <iostream>
const double PI=3.1415926;
class Container{
public:
Container(double radius){
this->radius=radius;
}
virtual double calSurfaceArea()=0;
virtual double calVolume()=0;
protected:
double radius;
};
class Cube: public Container{
public:
Cube(double radius):Container(radius){}
double calSurfaceArea(){
return radius*radius*6;
}
double calVolume(){
return radius*radius*radius;
}
};
class Sphere: public Container{
public:
Sphere(double radius):Container(radius){}
double calSurfaceArea(){
return radius*4*PI*radius;
}
double calVolume(){
return radius*radius*radius*PI*4/3;
}
};
class Cylinder: public Container{
public:
Cylinder(double radius, double height):Container(radius){
this->height=height;
}
double calSurfaceArea(){
return 2*PI*radius*radius+PI*2*radius*height;
}
double calVolume(){
return PI*radius*radius*height;
}
private:
double height;
};
int main()
{
Container *p;
double a, b, c1, c2;
cin>> a >> b >> c1 >> c2;
Cube A(a);
Sphere B(b);
Cylinder C(c1,c2);
p=&A;
cout << "Cube's area: " << p->calSurfaceArea() << ", volume: " << p->calVolume() << endl;
p=&B;
cout << "Sphere's area: " << p->calSurfaceArea() << ", volume: " << p->calVolume() << endl;
p=&C;
cout << "Cylinder's area: " << p->calSurfaceArea() << ", volume: " << p->calVolume() << endl;
return 0;
}
14-H2
栈 Stack
是一种先入后出的数据结构,最先入栈的元素称为栈底,最后入栈的元素称为栈顶。为了方便,可用 node.hpp 中的链表结构实现栈,并用链表头指向栈顶元素。
根据下述定义,请实现一个类模板 Stack
,使其可以保存不同类型的数据。
#include <iostream>
using namespace std;
template <typename ElementType>
class Stack
{
public:
Stack();
~Stack();
void push(ElementType obj);
void pop();
ElementType getTop() const;
bool isEmpty() const;
private:
struct Node // 栈结点类型
{
ElementType element; // 结点中存放的元素
Node *next; // 指向下一结点的指针
};
Node *top; // 栈顶
};
template <typename ElementType>
void Stack<ElementType>::push(ElementType obj)
{
Node *temp;
temp = new Node;
temp->element = obj;
temp->next = top;
top = temp;
}
template <typename ElementType>
void Stack<ElementType>::pop()
{
Node *temp;
if (top != nullptr)
{
temp = top;
top = top->next;
delete temp;
}
}
template <typename ElementType>
bool Stack<ElementType>::isEmpty() const
{
if (top != nullptr)
return false;
return true;
}
template <typename ElementType>
Stack<ElementType>::Stack(){
top = nullptr;
}
template <typename ElementType>
ElementType Stack<ElementType>::getTop() const{
return top->element;
}
template <typename ElementType>
Stack<ElementType>::~Stack(){
while(!isEmpty())
pop();
}
#include "genericStack.hpp"
#include <iostream>
using namespace std;
int main() //程序EX6_4.cpp
{
Stack<int> stack; // 实例化一个保存int型元素的栈
for (int i = 1; i < 9; i++) // 向栈中压入8个元素
stack.push(i);
while (!stack.isEmpty())
{ // 栈不为空时循环
cout << stack.getTop() << " "; // 显示栈顶元素
stack.pop(); // 弹出栈顶元素
}
return 0;
}
14-H3
根据 listnode.h 中对链表 listNode
的定义,在 orderedList.hpp 中实现以下三个类:
-
抽象基类
OrderedList
,表示一个整数链表,有以下方法:- 构造函数:无参数,创建一个空的链表;
void insert(int val)
向链表中插入一个元素,在OrderedList
中该方法应当为纯虚函数;void printList() const
依次在屏幕上输出链表中的元素,元素之间用空格分隔,输出完整个链表后换行;- 析构函数:释放链表所占用的空间。
此外,
OrderedList
还有一个保护成员:listNode * root;
-
具体类
AscendOrderedList
,表示一个升序的链表,继承自OrderedList
,需要重载void insert(int val)
函数,在插入的时候实现升序。 -
具体类
DescendOrderedList
,表示一个降序的链表,继承自OrderedList
,需要重载void insert(int val)
函数,在插入的时候实现降序。
#include <iostream>
struct listNode {
listNode * prev, * next;
int val;
listNode(): val(0), prev(nullptr), next(nullptr){}
listNode(int v, listNode *p, listNode *n): val(v), prev(p), next(n) {
if (prev != nullptr)
prev->next = this;
if (next != nullptr)
next->prev = this;
}
};
class OrderedList {
protected:
listNode * root;
public:
OrderedList(): root(nullptr) {}
virtual void insert(int val) = 0;
void printList() const {
for (const listNode * p = root; p != nullptr; p = p->next)
std::cout << p->val << ' ';
std::cout << std::endl;
}
virtual ~OrderedList() {
listNode * temp;
for (listNode * p = root; p != nullptr; delete temp) {
temp = p;
p = p->next;
}
}
};
class AscendOrderedList: public OrderedList {
public:
AscendOrderedList(): OrderedList() {}
void insert(int val) {
if (root == nullptr) {
root = new listNode(val, nullptr, nullptr);
}
else {
listNode * temp = nullptr;
for (listNode * p = root; p != nullptr && p->val < val; p = p->next)
temp = p;
if (temp == nullptr)
root = new listNode(val, nullptr, root);
else
temp = new listNode(val, temp, temp->next);
}
}
~AscendOrderedList() {}
};
class DescendOrderedList: public OrderedList {
public:
DescendOrderedList(): OrderedList() {}
void insert(int val) {
if (root == nullptr) {
root = new listNode(val, nullptr, nullptr);
}
else {
listNode * temp = nullptr;
for (listNode * p = root; p != nullptr && p->val > val; p = p->next)
temp = p;
if (temp == nullptr)
root = new listNode(val, nullptr, root);
else
temp = new listNode(val, temp, temp->next);
}
}
~DescendOrderedList() {}
};
int main() {
int n, x;
OrderedList * a = new AscendOrderedList, * d = new DescendOrderedList;
std::cin >> n;
for (int i = 0; i < n; ++i) {
std::cin >> x;
a->insert(x);
d->insert(x);
}
a->printList();
d->printList();
delete a;
delete d;
return 0;
}
15-k1
#include <vector>
#include <iostream>
using namespace std;
void push_back(vector<string>& v, const string& str) {
v.push_back(str);
}
void insert(vector<string>& v, const vector<string>::iterator& pos, const string& str) {
v.insert(pos, str);
}
void print(vector<string>& v) {
for(auto it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
void erase(vector<string>& v, const vector<string>::iterator& pos) {
v.erase(pos);
}
void pop_back(vector<string>& v) {
v.pop_back();
}
// 使用vector的push_back函数,在vector尾部插入
void push_back(vector<string>& v, const string& str);
// 使用vector的insert函数,在pos指向的位置插入
void insert(vector<string>& v, const vector<string>::iterator& pos, const string& str);
// 遍历vector输出所有元素,中间用空格隔开
void print(vector<string>& v);
// 使用vector的erase函数,删除pos指向的元素
void erase(vector<string>& v, const vector<string>::iterator& pos);
// 使用vector的pop_back函数,删除尾部元素
void pop_back(vector<string>& v);
int main() {
vector<string> v1;
vector<string> v2 = {"Thursday", "me"};
for(auto it = v2.begin(); it != v2.end(); it++) {
push_back(v1, *it);
}
push_back(v1, "50");
insert(v1, v1.begin(), "Crazy");
insert(v1, v1.begin() + 2, "V");
print(v1);
cout << "vector size: " << v1.size() << endl;
erase(v1, v1.end() - 1);
print(v1);
cout << "vector size: " << v1.size() << endl;
pop_back(v1);
print(v1);
cout << "vector size: " << v1.size() << endl;
return 0;
}
15-k2
#include <map>
#include <utility>
#include <iostream>
using namespace std;
void insert(map<string, int>& m, const string& key, int val);
void insert(map<string, int>& m, const pair<string, int>& p);
map<string, int>::iterator find(map<string, int>& m, const string& key);
void erase(map<string, int>& m, const string& key);
void erase(map<string, int>& m, const map<string, int>::iterator& pos);
// {key: val, key: val, key: val}
void print(const map<string, int>& m);
int main() {
map<string, int> m = {{"hello", 1}, {"hi", 2}};
insert(m, "www", 3);
insert(m, make_pair("h", 4));
print(m);
erase(m, "hello");
print(m);
map<string, int>::iterator pos = find(m, "www");
if (pos != m.end()) {
erase(m, pos);
}
print(m);
return 0;
}
void insert(map<string, int>& m, const string& key, int val) {
m.insert({key, val});
}
void insert(map<string, int>& m, const pair<string, int>& p) {
m.insert(p);
}
map<string, int>::iterator find(map<string, int>& m, const string& key) {
return m.find(key);
}
void erase(map<string, int>& m, const string& key) {
m.erase(key);
}
void erase(map<string, int>& m, const map<string, int>::iterator& pos) {
m.erase(pos);
}
void print(const map<string, int>& m) {
cout << '{';
auto it = m.begin();
while(it != m.end()) {
cout << it->first << ": " << it->second;
it++;
if(it != m.end()) {
cout << ", ";
}
}
cout << '}' << endl;
}
15-h2
Write a program to complete the following command:
new id
- create a specified sequence of Numbers for id (id < 200000)Add id num
- to join the sequence of Numbers for id integer nummerge id1 id2
- merger sequence number of id1 and id2, and make id2 emptyunique id
- remove repetitive element in the sequence idout id
- from small to large, output the elements of sequence of Numbers for ids separated by Spaces-
#include <iostream> #include <cstring> #include <map> #include <vector> #include <list> #include <algorithm> using namespace std; int main() { int n; cin>>n; map<int, list<int> > mp; while(n--){ string cmd; cin>>cmd; if(cmd == "new"){ int id; cin>>id; mp[id] = list<int>(); } if(cmd == "add"){ int id; int num; cin>>id>>num; mp[id].push_back(num); } if(cmd == "merge"){ int id1; int id2; cin>>id1>>id2; mp[id1].merge(mp[id2]); } if(cmd == "unique"){ int id; cin>>id; mp[id].sort(); mp[id].unique(); } if(cmd == "out"){ int id; cin>>id; mp[id].sort(); list<int>::iterator i; for(i=mp[id].begin(); i!=mp[id].end(); i++) cout<<*i<<" "; cout<<endl; } } return 0; }
15-h1
-
学校里有2个会议室,1个小会议室和1个大会议室。按照规定,小会议室和大会议室每天各自只有1个租借名额。如果要租借小会议室,只需要提供租借的日期信息(int类型)即可;如果要租借大会议室,那么不仅要提供租借的日期信息(int类型),而且还要同时提供租借该会议室的缘由(string类型)。
会议室的租借过程是无监督的和先到先得的。“无监督” 意味着只要某天某个会议室还没有人租借,那么任何人都可以申请此会议室该日期的使用权;“先到先得” 意味着如果会议室某天的使用权已经被其它人租借了,那么后续对于同一天同一个会议室的租借申请都会失败,除非原来的拥有者主动取消了这一次租借。
请根据提示完成RoomManager类,实现该学校里1大1小2个会议室的租借管理。
-
#include <map> #include <set> using namespace std; class RoomManager { private: // Conference Room 1, the smaller one. // Only require the date information for appointment. set<int> _room1; // Conference Room 2, the bigger one. // Require both date and event information for appointment. map<int, string> _room2; public: // Insert an appointment to the smaller Conference Room (1). bool InsertAppointment(const int& date); // Insert an appointment to the bigger Conference Room (2). bool InsertAppointment(const int& date, const string& event); // Cancel an appointment on designated date. // If room_id == 1, cancel the corresponding appointment of _room1. // If room_id == 2, cancel the corresponding appointment of _room2. bool CancelAppointment(const int& room_id, const int& date); // Print all existing appointments in chronological order. // If room_id == 1, just print the date information. Each piece of information is separated by ' '. // If room_id == 2, print the information in form of "date(event)". Each piece of information is separated by ' '. // If there is no even one existing appointment yet, print "No Appointment". void PrintAppointments(const int& room_id) const; }; bool RoomManager::InsertAppointment(const int& date) { if(_room1.find(date) != _room1.end()) { return false; } else { _room1.insert(date); return true; } } bool RoomManager::InsertAppointment(const int& date, const string& event) { if(_room2.find(date) != _room2.end()) { return false; } else { _room2[date] = event; return true; } } bool RoomManager::CancelAppointment(const int& room_id, const int& date) { if(room_id == 1) { set<int>::iterator sit = _room1.find(date); if(sit == _room1.end()) { return false; } else { _room1.erase(sit); return true; } } else { map<int, string>::iterator mit = _room2.find(date); if(mit == _room2.end()) { return false; } else { _room2.erase(mit); return true; } } } void RoomManager::PrintAppointments(const int& room_id) const { if(room_id == 1) { if(_room1.empty()) { cout << "No Appointment"; } else { for(auto sit=_room1.begin(); sit!=_room1.end(); ++sit) { cout << *sit << ' '; } } } else { if(_room2.empty()) { cout << "No Appointment"; } else { for(auto mit=_room2.begin(); mit!=_room2.end(); ++mit) { cout << mit->first << '(' << mit->second << ')' << ' '; } } } } int main() { RoomManager manager; int num_operations; string operation; cin >> num_operations; // How many operations in total. cout << endl; for(int i=1; i<=num_operations; ++i) { cin >> operation; if(operation == "InsertRoom1") { int date; cin >> date; cout << "Succeed?: " << boolalpha << manager.InsertAppointment(date) << endl; } else if(operation == "InsertRoom2") { int date; string event; cin >> date; getline(cin, event); event.erase(0, 1); cout << "Succeed?: " << boolalpha << manager.InsertAppointment(date, event) << endl; } else if(operation == "CancelRoom1") { int date; cin >> date; cout << "Succeed?: " << boolalpha << manager.CancelAppointment(1, date) << endl; } else if(operation == "CancelRoom2") { int date; cin >> date; cout << "Succeed?: " << boolalpha << manager.CancelAppointment(2, date) << endl; } else if(operation == "PrintRoom1") { manager.PrintAppointments(1); cout << endl; } else { manager.PrintAppointments(2); cout << endl; } } return 0; }
15-h3
-
使用stl中的单端队列
queue
实现栈 -
#include <queue> using namespace std; class mystack { private: queue<int> q1; queue<int> q2; public: mystack(); void push(int); int pop(); int top(); bool empty(); }; mystack::mystack(){} void mystack::push(int x) { q2.push(x); while (!q1.empty()) { int tmp = q1.front(); q1.pop(); q2.push(tmp); } while (!q2.empty()) { int tmp = q2.front(); q2.pop(); q1.push(tmp); } } int mystack::pop() { if (q1.empty()) { return -1; } int tmp = q1.front(); q1.pop(); return tmp; } int mystack::top() { if (q1.empty()) { return -1; } return q1.front(); } bool mystack::empty() { return q1.empty(); } void print(mystack& st) { while (!st.empty()) { int tmp = st.pop(); cout << tmp << ' '; } cout << endl; } int main() { mystack st; int n; cin >> n; while (n--) { string op; cin >> op; if (op == "push") { int x; cin >> x; st.push(x); cout << "push " << x << endl; } else if (op == "pop") { int res = st.pop(); if (res == -1) { cout << "pop failed" << endl; } else { cout << "pop " << res << endl; } } else if (op == "top") { int res = st.top(); if (res == -1) { cout << "top failed" << endl; } else { cout << "top " << res << endl; } } else if (op == "empty") { if (st.empty()) { cout << "st empty" << endl; } else { cout << "st not empty" << endl; } } } print(st); return 0; }
16-k1
-
栈这种数据结构在算法中的一个重要应用就是用于匹配,比如最经典的括号匹配问题,用于判断一串括号序列是否是合法的。理解一串括号序列是否合法很简单,在不考虑括号优先级的情况下,形如"()[]{}"、"[{}{}]"、"[()[[[]]]]"、"{{}}([]){{}}"这样的括号序列都可以视为是合法的,而形如"((]]"、"[{]}"、"{{{{"、")))[]"、"(({[]])"这样的括号序列都可以视为是不合法的。
现在给定一串括号序列(字符串类型),不需要你手动重新实现完整的Stack,请使用STL中的stack来完成这项工作!
-
#include <stack> #include <iostream> using namespace std; bool Matched(const string& brackets, const int& length) { stack<char> judger; for(int i=0; i<length; ++i) { if(brackets[i] == '(' || brackets[i] == '[' || brackets[i] == '{') { judger.push(brackets[i]); } else { if(judger.empty()) { return false; } else if(brackets[i] == ')' && judger.top() != '(') { return false; } else if(brackets[i] == ']' && judger.top() != '[') { return false; } else if(brackets[i] == '}' && judger.top() != '{') { return false; } else { judger.pop(); } } } return judger.empty(); } int main() { string brackets; cin >> brackets; int length = brackets.length(); cout << "Origin brackets are :" << endl; cout << ">>> " << brackets << endl; cout << "Your judge on whether those brackets are matched is :" << endl; cout << ">>> " << boolalpha << Matched(brackets, length) << endl; return 0; }
16-k2
-
有理数是非常基本的数。这里用
rational
类代表有理数。请实现一个排序函数sortRational
-
#include <vector> #include <string> #include <sstream> #include <algorithm> using namespace std; class rational { private: int a; int b; // value is \frac{a}{b} public: rational(int a, int b) : a(a), b(b) {}; bool operator > (const rational& other) const; string to_str() const; }; void sortRational(vector<rational>& vec); bool rational::operator > (const rational& other) const { auto c = other.a; auto d = other.b; return (b * d > 0 && a * d > b * c) || (b * d < 0 && a * d < b * c); } string rational::to_str() const { std::stringstream ss; ss << this->a << "/" << this->b; std::string s; ss >> s; return s; } void sortRational(vector<rational>& vec) { sort(vec.begin(), vec.end(), greater<rational>()); } void show(const vector<rational>& vec) { cout << "[ "; if (vec.size() > 0) { cout << vec[0].to_str(); for (int i = 1; i < vec.size(); ++i) { cout << ", "; cout << vec[i].to_str(); } } cout << " ]"; } int main() { vector<rational> v1 = { rational{1, 2}, rational{1, 3}, rational{1, 5}, rational{2, 9}, rational{1, 7} }; vector<rational> v2 = { rational{-1, 2}, rational{-2, 1}, rational{1, 3}, rational{2, 1} }; sortRational(v1); sortRational(v2); show(v1); cout << "\n"; show(v2); }
在程序语言中,多态特指一个标识符指代不同类型的数据。或者说,由具体的数据类型决定该标志符的语义。
-
满足多态定义:函数重载,方法覆盖,泛型/模板
-
基类指针指向派生类对象,这对于实现多态性的行为是至关重要的
-
纯虚函数:virtual void eat()=0;
不建议给定义,但是纯虚函数是析构函数必须提供定义
抽象类:定义或继承了至少一个纯虚函数的类
不能被实例化,子类是抽象类,?除非所有纯虚函数都被覆写为非纯虚函数
抽象类能且只能作为基类指针或引用,因为不可能存在抽象对象,可以申明为指针或引用,指代自己派生类对象
虚函数没有定义是链接错误,回避new 包含虚函数的类 (在抽象类与接口案例中,多态类型指针指针必须动态转化为对象实际类型指针才能正确执行对象析构过程)。
基类声明其析构函数为virtual,则派生的析构函数始终覆盖它。
任何包含虚函数的基类的析构函数必须公开为虚,或受保护且非虚。否则很容易导致内存泄漏。
typeid:返回指针和引用所指的实际类型(求值:多态类型对象的泛左值表达式)
dynamic_cast 将基类类型的指针或引用安全的转换为其派生类类型的指针或引用:沿继承层次向上、向下及侧向,安全地转换到其他类型的指针和引用。dynamic_cast<新类型>
final指定某个虚函数不能在子类中被覆盖,某个类不能被子类继承
泛型编程:独立于任何特定数据类型的编程,使得不同类型的数据对象可以被相同的代码操作,函数模板,类模板。
函数模板的一般形式
template<模板形参表>
返回值类型 函数名 (形式参数列表)
{函数体语句;}
函数模板的实例化,不进行常规的隐式类型转换。
显示(全)模板特化。
模板重载:1.定义名字相同而函数形参表不同的函数模板,或者定义与函数模板同名的非模板函数(正常函数),在其函数体中完成不同的行为。
函数调用的静态绑定规则:非模板->模板->隐式转换后非模板->编译错误
类模板的一般形式:
template<模板形参表>
class 类模板名
{类成员声明}
在类模板外定义成员函数的一般形式:
template<模板形参类>
返回值类型 类模板名<模板形参名列表>::函数名(函数新参表){函数实现}
非类型模板形参:相当于模板内部的常量,?对应的模板实参必须是编译时常量表达式
c++的IO文件操作
流充当了程序和流源或流目标之间的桥梁。这使得C++程序可以以相同的方式对待来自键盘的输入和来自文件的输入。
缓冲区:一旦按enter回车键。缓冲区里的数据才流入程序,在按回车键之前,我们可以修改缓冲区的数据。
错误流没有缓冲区,避免缓冲数据丢失。
c++一切文件操作都是定义在类里的成员函数,c++的文件IO有以下类:ofstream写(输出);ifstream读(输入);fstream可同时读写操作文件。
如果未调用函数close,在source销毁时调用的析构函数也会自动调用close()
#include<fstream>
//ifstream和ofstream定义在这个头文件里面
using namespace std;
int main(){
int someInt;
float someFloat;
char someChar;
ifstream inFile;
ofstream outFile;
//定义两个对象,前者负责文件输入,后者负责文件输出
inFile.open("Source.txt");
//把输入文件流inFile与文件source.tet关联起来,后面从inFile输入数据便是从文件Source.txt里读取数据
outFile.open("Result.txt");
//把输出文件流outFile与文件result.tet关联起来,后面输出的结果放入outFile中最终就会保存到Result.txt里面
//把文件流与具体的文件相关联起来,使得后续的具体的读写操作作用在这些文件之上
inFile>>someInt>>someFloat>>someChar;
//从输入文件流inFile中读取数据,置入变量中
outFile<<"The answer is:"<<someInt*someFloat<<endl;
//把要输出的内容放入输出文件流里面,最终保存到文件result.txt里面
inFile.close();
outFile.close();
return 0;
}
在c++的ifstream里open函数还有第二个参数表示打开方式
void open(const char*filename,openmode mode);
mode的表示:iOS::
out:文件以输出方式打开
in:文件以输入方式打开
ate:初始位置:文件尾
app:所有输出附加在文件末尾
trunc:如果文件已存在则先删除该文件
binary:二进制方式
类参数的默认方式:
ofstream ios::out|ios::trunc
ifstream ios::in
fstream ios::in|ios::out
如果open函数只有文件名一个参数,以读/写普通文件1打开
c++IO系统管理两个与一个文件相联系的指针,一个是读指针,另一个是写指针
seekg()设置读位置,seekp设置写位置。
STL:容器库,迭代器库,算法库
vector<T>是动态的连续数组。注意迭代器的指向,迭代器是用于遍历容器元素的指针对象的包装。、
程序员在创建适配器对象时可以选择相应的基础容器类。
1)stack:vector,list,deque(默认)
2)queue:list,deque(默认)
3)priority_queue:vector(默认),deque
迭代器
throw表达式可以是任意类型的对象。
子类必须优先基类catch,(...)必须是最后catch
用基类引用类型捕捉异常,且虚函数才能产生多态
异常传播:当前函数未处理异常,交给调用函数