NJU OOP三道好题

T1

  • 任务描述

    • 智能指针是C++标准库提供的一种机制,用于防止内存泄漏,及时回收不再需要的内存。

    • 当我们使用普通的指针变量时,一旦程序变得冗长复杂,难免会出现忘记释放内存的情况,导致可用的内存变少。

    • 如果使用智能指针,那么智能指针内部会维护一个指针指向目标地址,和一个相关联的计数器指针(他们在内存上的地址并无关联,只有逻辑上的联系)。计数器统计持有目标地址的指针数量,一旦计数器为0,那么就可以认为没有任何指针可以访问到该地址,该地址将不再被需要,此时,就可以释放内存。

    • 这样,我们就不需要关心内存的释放,而由智能指针选择在合适的时机释放内存。

    • 本关要求你补全一个伪智能指针类SmartPointer,为了简单起见,本关中该智能指针类只能指向动态分配内存的Node类型的对象,并且一个Node对象地址只会用于初始化一个智能指针(Node类型是我们定义的占位类型,一个Node对象表示一块需要SmartPointer管理的空间)。

    • 它在指针类内部维护两个关键成员变量:

    1
    2
    Node* pointer; 
    int* ref_cnt;
    1. Node类型的指针变量pointer
    2. 一个指向整型变量的指针ref_cnt,它指向的值*ref_cnt表示当前拥有pointer所指向的Node对象地址的SmartPointer对象数。
  • 代码

    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    #include <iostream>
    class Node;
    class SmartPointer
    {
    Node *pointer;
    int *ref_cnt;

    public:
    SmartPointer()
    {
    // 空指针
    pointer = nullptr;
    ref_cnt = nullptr;
    }
    SmartPointer(Node *p)
    {
    pointer = p;
    ref_cnt = new int(1);
    }

    // 需要完成的函数
    SmartPointer(const SmartPointer &sptr);
    void assign(const SmartPointer &sptr); // 指针赋值,将sptr赋值给本指针
    ~SmartPointer(); // 析构函数,注意:为通过测试样例,需要保证析构函数可以被重复调用
    };

    class Node
    {
    int id;

    public:
    Node(int id)
    {
    this->id = id;
    }
    ~Node()
    {
    std::cout << id << ' ';
    }
    };
    SmartPointer::SmartPointer(const SmartPointer &sptr)
    {
    if (sptr.ref_cnt)
    {
    (*(sptr.ref_cnt))++;
    }
    pointer = sptr.pointer;
    ref_cnt = sptr.ref_cnt;
    }

    void SmartPointer::assign(const SmartPointer &sptr)
    {
    int flag=0;
    if (pointer && ref_cnt)
    {
    (*ref_cnt)--;
    if (sptr.ref_cnt)//先加防止自赋值情况
    {
    (*(sptr.ref_cnt))++;
    flag=1;
    }
    if (*ref_cnt == 0)
    {
    delete pointer;
    pointer=nullptr;
    }
    }
    if (sptr.ref_cnt&&!flag)
    {
    (*(sptr.ref_cnt))++;
    }
    pointer = sptr.pointer;
    ref_cnt = sptr.ref_cnt;
    }

    SmartPointer::~SmartPointer()
    {
    if (pointer)
    {
    (*ref_cnt)--;
    if(*ref_cnt==0) delete pointer, delete ref_cnt;
    }
    pointer = nullptr;
    ref_cnt = nullptr;
    }

T2

  • 迭代器是容器类(如数组,链表,队列,栈等)提供的、用于遍历和访问容器内对象的辅助类。在本题中,我们考虑一种简化的迭代器,它为我们自定义的整型数组类提供元素的遍历和访问。

  • 自定义整型数组根据参数创建和维护特定长度的动态整型数组,并且提供迭代器Iterator来访问数组arr中的元素。由于迭代器常与特定类关联,所以我们使用MyArray的内部类来实现Iterator

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// TODO: finish the following class, and override the corresponding operator

class MyArray
{
int *arr;//数组本身
int size;//数组长度

public:
class Iterator//类内定义迭代器类
{
private:
int cnt;//显示迭代器对象当前所指的数组位置
MyArray *target;//指向需要维护的数组,注意这里不能使用成员对象,而只能使用成员对象指针

public:
Iterator(int k, MyArray *a)//迭代器的构建,根据需要传入初始位置和需维护数组
{
cnt = k;
target = a;
}
bool get(int &value) const//获得迭代器当前指向位置的值,若能找到,返回true,若当前迭代器指 向非数组合法区域,返回false
{
if (target && cnt >= 0 && cnt < target->size)
{
value = target->arr[cnt];
return true;
}
else
{
return false;
}
}
bool put(int value)//向迭代器当前指向位置的元素赋值,若能找到,返回true,若当前迭代器指 向非数组合法区域,返回false
{
if (target && cnt >= 0 && cnt < target->size)
{
target->arr[cnt] = value;
return true;
}
else
{
return false;
}
}
Iterator &operator++()//重载前置操作符++,实现自增
{
cnt++;
return *this;
}
const Iterator operator++(int)//重置后置操作符++
{
Iterator temp = *this;
cnt++;
return temp;
}
Iterator &operator--()//重置前置操作符--
{
cnt--;
return *this;
}
const Iterator operator--(int)//重置后置操作符--
{
Iterator temp = *this;
cnt--;
return temp;
}
bool operator==(const Iterator &other)//重置二元操作符==,判断两个迭代器是否指向同一位置
{
if (target == other.target && cnt == other.cnt)
{
return true;
}
else
{
return false;
}
}
bool operator!=(const Iterator &other)
{
return !(*this == other);
}
Iterator operator+(int m)//重置操作符+,返回在当前迭代器位置基础上后移若干个位置的新迭代器
{
Iterator temp = *this;
temp.cnt += m;
return temp;
}
Iterator operator-(int m)//重置操作符-,返回在当前迭代器位置基础上前移若干个位置的新迭代器
{
Iterator temp = *this;
temp.cnt -= m;
return temp;
}
};

public:
MyArray(int sz)//创建需维护数组的对象
{
size = sz;
arr = new int[size];
}
~MyArray()//析构,返还申请空间
{
size = 0;
delete[] arr;
arr = nullptr;//注意悬浮指针
}
Iterator begin()//返回该数组对象的开始迭代器,指向第0位
{
if (size > 0 && arr != nullptr)
{
return Iterator(0, this);
}
else//考虑已析构的数组对象,或size=0的情况,此时返回的迭代器指向nullptr
{
return Iterator(0, nullptr);
}
}
Iterator end()
{
if (size > 0 && arr != nullptr)//返回该数组对象的尾部迭代器,指向第size位
{
return Iterator(size, this);
}
else
{
return Iterator(size, nullptr);
}
}
};

T3

  • Plant2类的植物有两个时间节点,time1time2,该类植物从种植开始,生长time1之后,每天便可以生产一株新的需要立马种植的完全相同的植物(可以看作每个新植物都会立马被种植),生长time2之后,该植物就会死亡。例如某植物在第一天种植,time1=1,time2=2,那么会在第二天开始生产新植物,在第三天死亡(第三天不能生产新植物)。
1
2
3
4
5
6
7
8
9
Plant2 t(4, 1, 3); //目前已有存活的植物t
t.func(4); //植物t被种植,即开始发挥功能,距离关卡结束还有4天
cout << getTotalPlant2Num(); //关卡结束,输出目前存活的植物数量
//func函数调用的时候即为将植物t种植下去经过4天,该种植物的time1=1,time2=3.
//第一天:植物t生长,但未生出新植物
//第二天:植物t生长超过1天,生出新植物t2
//第三天:植物t生出植物t3,植物t2生长超过1天,生出新植物t4
//第四天:植物t生长超过3天,死亡。植物t2生出t5,植物t3生出t6,植物t4生出t7
//目前有6株植物存活。
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
class Plant2
{
private:
static int num2;
//=========
int time1 = 0;
int time2 = 0;
public:
friend int getTotalPlant2Num();
//=========
Plant2(int time1, int time2); // 需要完成的构造函数
// 该植物类的功能,n表示该植物从种植开始经过的天数,n可能大于time2,具体可看示例
void func(int n);
};
// 获得Plant2的总数量
int getTotalPlant2Num();
int Plant2::num2 = 0;
Plant2::Plant2(int time1, int time2)
{
this->time1 = time1;
this->time2 = time2;
num2++;
}

void Plant2::func(int n)
{
int cnt = 0, flag = 1;
while (cnt < n)//一株植物到游戏结束的总时长
{
if (cnt >= time2)//已经经过超过time2天,死亡,不再生成新植物
{
if (flag)//当第一次到达该时间点,植物数量减一
{
num2--;
flag = 0;
}
}
if (cnt >= time1 && flag == 1)//到达time1时间,且未死亡
{
Plant2 a(time1, time2);//构造新植物,数量加一
a.func(n - cnt);//新植物剩下的天数为生成他的植物的创造时剩余天数减去该植物已经过的天数
}
cnt++;
}
}
int getTotalPlant2Num()
{
return Plant2::num2;//返回植物的总数
}
  • 今天的内容就到这里啦,感谢几位NJU同学的分享~欢迎在评论区继续留言哦O(∩_∩)O