从零开始的上机(5)

本次上机涉及内容:类与对象、运算符重载、继承、多态

Hero和Priest

描述

根据输出完善程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
class Hero {
public:
// 在此处补充你的代码
};
class Priest: public Hero {
public:
    virtual void attack() { cout << "Priest::attack()" << endl; }
    virtual void defense() { cout << "Priest::defense()" << endl; } 
}; 

int main() {
    Priest anduin;
    Hero h;
    h.defense();
    Hero *player = &anduin;
    player->attack();
    player->defense();
    anduin.attack();
    anduin.defense();
    return 0;
}

输入

1
None

输出

1
2
3
4
5
Hero::defense()
Hero::attack()
Priest::defense()
Priest::attack()
Priest::defense()

Solution

观察代码,发现Priest类由Hero类派生,从而这里需要用到多态的相关知识。

再看主函数,首先调用h.defense(),再创建了一个指向Priest对象的Hero*指针,那么此时想必要使用多态了。

观察输出,发现Attack调用的是Hero的,而Defense则调用的是Priest的,即Defense是虚函数/多态,而Attack不是。

 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
#include <iostream>
using namespace std;
class Hero {
public:
    virtual void defense() { cout << "Hero::defense()" << endl; }
    void attack() { cout << "Hero::attack()" << endl; }
    // 在此处补充你的代码
};
class Priest : public Hero {
public:
    virtual void attack() { cout << "Priest::attack()" << endl; }
    virtual void defense() { cout << "Priest::defense()" << endl; }
};

int main() {
    Priest anduin;
    Hero h;
    h.defense();
    Hero *player = &anduin;
    player->attack();
    player->defense();
    anduin.attack();
    anduin.defense();
    system("pause");
    return 0;
}

统计动物数量

描述

根据输出完善程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
// 在此处补充你的代码
void print() {
	cout << Animal::number << " animals in the zoo, " << Dog::number << " of them are dogs, " << Cat::number << " of them are cats" << endl;
}

int main() {
	print();
	Dog d1, d2;
	Cat c1;
	print();
	Dog* d3 = new Dog();
	Animal* c2 = new Cat;
	Cat* c3 = new Cat;
	print();
	delete c3;
	delete c2;
	delete d3;
	print();
}

输入

1

输出

1
2
3
4
0 animals in the zoo, 0 of them are dogs, 0 of them are cats
3 animals in the zoo, 2 of them are dogs, 1 of them are cats
6 animals in the zoo, 3 of them are dogs, 3 of them are cats
3 animals in the zoo, 2 of them are dogs, 1 of them are cats

Solution

观察代码,发现这里的number应该是Animal类、 Dog类和Cat类的某个静态成员变量,应该是来统计当前动物的数量的。

再看输出,可以发现每次创建一个对象时,总动物数量和对应动物的数量都需要加一,这应该写在构造函数里。

然后这里需要注意的是,需要将基类的析构函数写成虚函数形式,这样,在delete派生类对象的时候才会先执行派生类析构函数,再执行基类的析构函数,析构得干净。

还有一点要注意的是,由于几个number变量要初始化为0,因此需要在外面声明一下,这里的格式需要注意,可能忘了。

 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
#include <iostream>
using namespace std;
class Animal {
public:
    static int number;
    Animal() { number += 1; }
    virtual ~Animal() { number--; }
};
class Dog : public Animal {
public:
    static int number;
    Dog() { number += 1; }
    ~Dog() { number--; }
};
class Cat : public Animal {
public:
    static int number;
    Cat() { number += 1; }
    ~Cat() { number--; }
};
int Animal::number = 0;
int Dog::number = 0;
int Cat::number = 0;
// 在此处补充你的代码
void print() {
    cout << Animal::number << " animals in the zoo, " << Dog::number << " of them are dogs, " << Cat::number << " of them are cats" << endl;
}

int main() {
    print();
    Dog d1, d2;
    Cat c1;
    print();
    Dog* d3 = new Dog();
    Animal* c2 = new Cat;
    Cat* c3 = new Cat;
    print();
    delete c3;
    delete c2;
    delete d3;
    system("pause");
    print();
}

还是Fun和Do

描述

根据输出完善代码

 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
#include <iostream> 
using namespace std;

class A { 
	public: 
		virtual void Fun() { 
			cout << "A::Fun" << endl; 
		}; 
		virtual void Do() { 
			cout << "A::Do" << endl; 
		} 
};
// 在此处补充你的代码
{ 
	p.Fun(); 
	p.Do(); 
} 

void Call2(B p) {
	p.Fun();
	p.Do();
}



int main() { 
	C c;
	B b;
	Call1(b);
	Call1(c); 
	Call2(c);
	return 0;
}

输入

1
None

输出

1
2
3
4
5
6
A::Fun
B::Do
C::Fun
C::Do
A::Fun
B::Do

Solution

观察代码,发现是要完善B类、C类和Call1函数的定义。

从基类A的定义看出,这题是很明显的虚函数/多态题目。

再观察输出,可以发现传进B类对象的却输出了A::Fun,因此B应该没有自己的Fun函数,但是有自己的Do函数。

往下看,Call1(c)传入一个C类对象,输出C::FunC::Do,由于Call1能传进B类对象,因此参数类型必然不是C&C,于是得出结论:传入的是B&类型,虚函数调用C中对应函数。

最后,再用Call2(c)验证刚才的猜想就行~

 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
#include <iostream>
using namespace std;

class A {
public:
    virtual void Fun() { cout << "A::Fun" << endl; }
    virtual void Do() { cout << "A::Do" << endl; }
};
class B : public A {
public:
    void Do() { cout << "B::Do" << endl; }
};
class C : public B {
public:
    void Fun() { cout << "C::Fun" << endl; }
    void Do() { cout << "C::Do" << endl; }
};
void Call1(B &p)
// 在此处补充你的代码
{
    p.Fun();
    p.Do();
}

void Call2(B p) {
    p.Fun();
    p.Do();
}

int main() {
    C c;
    B b;
    Call1(b);
    Call1(c);
    Call2(c);
    system("pause");
    return 0;
}

多态

描述

根据输出完善代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

class Base {
public:
   virtual Base& fun() { cout << "base fun" << endl; return *this; }
   virtual Base& foo() { cout << "base foo" << endl; return *this; }
};

class Derived: public Base {
public:
   Base& fun() { cout << "derived fun" << endl; return *this; }
   Base& foo() { cout << "derived foo" << endl; return *this; }
};

Base& foo();
Base& fun();
// 在此处补充你的代码
int main() {
   foo().fun().foo();
   fun().foo().fun();
   return 0;
}

输入

1
None

输出

1
2
3
4
5
6
derived foo
derived fun
derived foo
base fun
base foo
base fun

Solution

首先观察到题目中有虚函数,因此可能发生多态。

这里比较少见的是Base& foo();这个语句,它是用来先声明这个函数的定义的,后续需要再次实现函数的具体内容。

于是,题目的要求就变成实现foofun函数了,它们都返回一个Base&对象。

要返回对象引用,自然需要在函数中声明一个,这道题最需要注意的点是,声明的这个对象必须要是静态对象,为什么?

因为这个静态对象只能创建一次,并且在函数结束后不会被销毁,否则原来的临时变量会被销毁,根本没有引用这一说。

随后再观察输出,便能写出代码了,此处为了简化作了压行。

 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
#include <iostream>
using namespace std;

class Base {
public:
   virtual Base& fun() { cout << "base fun" << endl; return *this; }
   virtual Base& foo() { cout << "base foo" << endl; return *this; }
};

class Derived: public Base {
public:
   Base& fun() { cout << "derived fun" << endl; return *this; }
   Base& foo() { cout << "derived foo" << endl; return *this; }
};

Base& foo();
Base& fun();
Base& foo(){
   static Derived d;
   return d.foo();
}
Base& fun(){
   static Base b;
   return b.fun();
}
// 在此处补充你的代码
int main() {
   foo().fun().foo();
   fun().foo().fun();
   system("pause");
   return 0;
}

又双叒叕是Fun和Do!

描述

根据输出完善代码。

 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 <iostream>
using namespace std;

class A{
public:
    int n;
    A(int m):n(m) {}
    virtual void Do(){
        cout <<"A::Do()" << n << endl;
        Fun();
    }
// 在此处补充你的代码
void CallFun(){
        Fun();
    }
};

class B: public A{
public:
    B(int n):A(n){ }
    void Do(){
        cout <<"B::Do()" << ++n << endl;
        CallFun();
    }
    void Fun(){
        cout <<"B::Fun()" << ++n << endl;
    }
};

int main(){
    B bb(1);
    A aa = bb;
    aa.Do();
    A *bp = &bb;
    bp->Do();
    return 0;
}

输入

1
None

输出

1
2
3
4
A::Do()1
A::Fun()2
B::Do()2
B::Fun()3

Solution

观察代码,可以发现需要补充的是AFun部分,而且BDo函数中,有一个CallFun的调用,ADo中,有一个Fun的调用。

于是,根据BFun的写法,抄一遍到A中来即可。

再往下看,A* bp=&bb,这是非常典型的多态写法,由输出可以看出,它调用的是BDo函数,却输出的是B::Fun,因此A中的Fun应该是虚函数。

 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
#include <iostream>
using namespace std;

class A {
public:
    int n;
    A(int m) : n(m) {}
    virtual void Do() {
        cout << "A::Do()" << n << endl;
        Fun();
    }
    virtual void Fun() { cout << "A::Fun()" << ++n << endl; }
    // 在此处补充你的代码
    void CallFun() { Fun(); }
};

class B : public A {
public:
    B(int n) : A(n) {}
    void Do() {
        cout << "B::Do()" << ++n << endl;
        CallFun();
    }
    void Fun() { cout << "B::Fun()" << ++n << endl; }
};

int main() {
    B bb(1);
    A aa = bb;
    aa.Do();
    A *bp = &bb;
    bp->Do();
    system("pause");
    return 0;
}

变来变去的数

描述

根据输出完善代码。

 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
#include <iostream>
using namespace std;

class myobject {
public:
// 在此处补充你的代码
};

class producer : public myobject {
public:
	virtual void work() {
		counter = counter + 5;
		print_avaliable();
	}
};

int myobject::counter = 0;

int main(){
	producer *pro = new producer();
	myobject *con = new myobject();
	pro->work(); pro->work(); cout << endl;
	con->work(); con->work(); con->work(); cout << endl;
	pro->work(); cout << endl;
	con->work(); con->work(); cout << endl;
}

输入

1
None

输出

1
2
3
4
5 10
6 2 2
7
3 3

Solution

首先,观察到producer类中有一个print_avaiable函数,因此需要在myobject内实现。

接着,观察样例,发现myobjectwork有以下规律:

$$ f(x)= \begin{cases} x-4, \quad x \geq 4 \\ x, \quad 0 \leq x \leq 3 \\ \end{cases}$$

于是完善代码即可。

 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
#include <iostream>
using namespace std;

class myobject {
public:
    static int counter;
    void work() {
        if (counter >= 4) counter -= 4;
        cout << counter << ' ';
    }
    void print_avaliable() { cout << counter << ' '; }
    // 在此处补充你的代码
};

class producer : public myobject {
public:
    virtual void work() {
        counter = counter + 5;
        print_avaliable();
    }
};

int myobject::counter = 0;

int main() {
    producer *pro = new producer();
    myobject *con = new myobject();
    pro->work();
    pro->work();
    cout << endl;
    con->work();
    con->work();
    con->work();
    cout << endl;
    pro->work();
    cout << endl;
    con->work();
    con->work();
    cout << endl;
    system("pause");
    return 0;
}

MyClass

描述

根据输出完善代码。

 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
#include <iostream>
using namespace std;
class CMyClassA {
	int val;
public:
	CMyClassA(int);
	void virtual print();
};
CMyClassA::CMyClassA(int arg) {
	val = arg;
	printf("A:%d\n", val);
}
void CMyClassA::print() {
	printf("%d\n", val);
	return;
}
// 在此处补充你的代码
int main(int argc, char** argv) {
	CMyClassA a(3), *ptr;
	CMyClassB b(5);
	ptr = &a; ptr->print();
	a = b;
	a.print();
	ptr = &b; ptr->print();
	return 0;
}

输入

1
None

输出

1
2
3
4
5
6
A:3
A:15
B:5
3
15
5

Solution

这道题,由a=b,很容易可以看出CMyClassB应该是继承自CMyCalss类的。

再观察输出,a.print()输出A:15,代表B类初始化时用传入值的三倍,顺便初始化了A类构造函数,注意这里的写法,可能已经忘了。同时B类和A类的构造函数都需要输出当前值,而先执行的是基类构造函数,因此先输出A::15

接下来完善print函数,并用剩下的样例验证即可。

 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
#include <iostream>
using namespace std;
class CMyClassA {
    int val;

public:
    CMyClassA(int);
    void virtual print();
};
CMyClassA::CMyClassA(int arg) {
    val = arg;
    printf("A:%d\n", val);
}
void CMyClassA::print() {
    printf("%d\n", val);
    return;
}
class CMyClassB : public CMyClassA {
public:
    int val;
    CMyClassB(int x) : CMyClassA(3 * x) {
        val = x;
        printf("B:%d\n", val);
    }
    void print() { printf("%d\n", val); }
};
// 在此处补充你的代码
int main(int argc, char** argv) {
    CMyClassA a(3), *ptr;
    CMyClassB b(5);
    ptr = &a;
    ptr->print();
    a = b;
    a.print();
    ptr = &b;
    ptr->print();
    system("pause");
    return 0;
}

输出指定结果二

描述

根据输出完善程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <map>
using namespace std;
// 在此处补充你的代码
int A::count = 0;
void func(B b) {  }
int main()
{
	A a1(5),a2;
	cout << A::count << endl;
	B b1(4);
	cout << A::count << endl;
	func(b1);
	cout << A::count << endl;
	A * pa = new B(4);
	cout << A::count << endl;
	delete pa;
	cout << A::count << endl;
	return 0;
}

输入

1
None

输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
2
3
B::destructor
A::destructor
3
4
B::destructor
A::destructor
3
B::destructor
A::destructor
A::destructor
A::destructor

Solution

观察代码,很容易发现A类有一个静态成员变量count,同时它有默认构造函数和转换构造函数,并且每次新建变量count会加一。

再往下看,发现B新建时A也增加了,因此BA的派生类,而count用于统计的是A的数量。

再往下看,func传入一个B型变量,注意此时编译器的行为是,将当前B型变量通过拷贝构造函数拷贝出一个新的B型变量并传入,因此结合count的含义,需要实现A类的拷贝构造函数。

最后,需要实现两者的析构函数以打印所需,注意到B类先于A类析构,因此A类析构函数应该是虚析构函数。

 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 <iostream>
#include <map>
using namespace std;
class A {
public:
    static int count;
    A(int x) { count++; }
    A() { count++; }
    A(const A& other) { count++; }
    virtual ~A() {
        cout << "A::destructor" << endl;
        count--;
    }
};
class B : public A {
public:
    B(int x) {}
    ~B() { cout << "B::destructor" << endl; }
};
// 在此处补充你的代码
int A::count = 0;
void func(B b) {}
int main() {
    A a1(5), a2;
    cout << A::count << endl;
    B b1(4);
    cout << A::count << endl;
    func(b1);
    cout << A::count << endl;
    A* pa = new B(4);
    cout << A::count << endl;
    delete pa;
    cout << A::count << endl;
    system("pause");
    return 0;
}

构造与析构

描述

根据输出完善程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
// 在此处补充你的代码
int main(){
	int n;
	cin >> n;
	for (int i = 1; i<=n; i++) {
		int x;
		cin >> x;
		if (x == 0) {
			A *a = new A();
			delete a;
		}else {
			A *a = new B();
			delete a;
		}
	}
	return 0;	
}

输入

1
2
3
1 0 1

输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
New A
New B
Delete B
Delete A
New A 
Delete A
New A
New B
Delete B
Delete A

Solution

观察主函数代码,发现需要实现A类和B类,并且由A* a=new B()得知,BA的派生类。

接着,观察输出,发现A先于B被构造,并且B先于A被析构,因此A中析构函数为虚析构函数,再根据输出填入打印语句即可。

 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 <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
class A {
public:
    A() { cout << "New A" << endl; }
    virtual ~A() { cout << "Delete A" << endl; }
};
class B : public A {
public:
    B() { cout << "New B" << endl; }
    ~B() { cout << "Delete B" << endl; }
};
// 在此处补充你的代码
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        if (x == 0) {
            A *a = new A();
            delete a;
        } else {
            A *a = new B();
            delete a;
        }
    }
    system("pause");
    return 0;
}

卖肉付牛肉

描述

根据输出完善程序。

 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
#include<bits/stdc++.h>
using namespace std;

class Food {
public:
// 在此处补充你的代码
};

class Meat : public Food {
public:
	void Buy() { cout << "Buy Meat" << endl; }
	virtual void Pay() { cout << "Pay Meat" << endl; }
};

class Beef : public Meat {
public:
	void Buy() { cout << "Buy Beef" << endl; }
	void Pay() { cout << "Pay Beef" << endl; }
};
int main() {
	Beef bf;
	Meat* pmt = &bf;
	Food* pfd = &bf;

	pfd->Buy();
	pfd->Pay();
	pmt->Buy();
	pmt->Pay();
	return 0;
}

输入

1
None

输出

1
2
3
4
Buy Nothing
Pay Beef
Buy Meat
Pay Beef

Solution

观察代码结构,发现主函数有一个Meat*->Beef,还有一个Food*->Beef

先调用Food*->BeefBuy,它输出了Buy Nothing,对比发现这应该是Food中的Buy函数,并且由定义看出,它不是虚函数。

再看Food*->BeefPay函数,它调用的是Beef->Pay,因此Food类的这个函数应该是虚函数,至于它应该输出什么,那不重要了。

接下来只需要用剩下的样例验证即可。

 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
#include <bits/stdc++.h>
using namespace std;

class Food {
public:
    void Buy() { cout << "Buy Nothing" << endl; }
    virtual void Pay() {}
    // 在此处补充你的代码
};

class Meat : public Food {
public:
    void Buy() { cout << "Buy Meat" << endl; }
    virtual void Pay() { cout << "Pay Meat" << endl; }
};

class Beef : public Meat {
public:
    void Buy() { cout << "Buy Beef" << endl; }
    void Pay() { cout << "Pay Beef" << endl; }
};
int main() {
    Beef bf;
    Meat* pmt = &bf;
    Food* pfd = &bf;

    pfd->Buy();
    pfd->Pay();
    pmt->Buy();
    pmt->Pay();
    system("pause");
    return 0;
}
本博客已稳定运行
发表了43篇文章 · 总计290.94k字
使用 Hugo 构建
主题 StackJimmy 设计