cs106l assignment 2025spring

assignment5 treebook

Part 1: Viewing Profiles

分析

就一个要求,编写一个将 User 打印到 std::ostream operator<< 方法,即重载运算符<<(此运算符应在 user.h 中声明为友元函数)
要求的效果

User(name=Alice, friends=[Bob, Charlie])

实现

首先在user.h中声明

 friend std::ostream& operator<<(std::ostream& os, User& user);

在user.cpp中实现

std::ostream& operator<<(std::ostream& os, User& user){
  os<<"User(name="<<user.get_name()<<", friends=[";
  std::string* friends = user._friends;
  size_t size = user.size();
  for(size_t i = 0; i < size; ++i){
    if(i+1 < size){
    os<<friends[i]<<", ";
  }else{
    os<<friends[i];
  }
}
  os<<"])";
  return os;
}

一些细节

为什么是引用 (&) 而不是值?
禁止拷贝:std::ostream 是禁止复制的(拷贝构造函数被删除)。

效率:返回引用避免不必要的拷贝开销。

修改原始流:需直接操作原始流对象(如 std::cout),而非其副本。

Part 2: Unfriendly Behaviour

为 User 类实现我们自己的特殊成员函数 (SMF) 版本,并删除一些编译器生成的版本无法满足要求的函数。

1.为 User 类实现析构函数。为此,请实现 ~User() SMF

成员变量

private:
  std::string _name;
  std::string* _friends;
  size_t _size;
  size_t _capacity;

_friends 是动态分配的资源:
_friends 是 std::string* 类型,指向通过 new[] 动态分配的字符串数组(从析构函数的 delete[] 可以推断)。这类动态分配的内存不会被编译器自动释放,必须手动通过 delete[] 回收,否则会导致内存泄漏。其他成员无需手动释放。

User::~User(){
  delete[] _friends;
};

在 C++ 中,析构函数用于对象销毁时释放资源。以下情况需要手动重写析构函数:

1.类中包含动态分配的内存(new/new[] 分配)

  • 当类成员是通过 new 分配的单个对象,或 new[] 分配的数组(如 std::string* _friends),必须在析构函数中用 delete 或 delete[] 释放,否则会导致内存泄漏。
    2.类持有文件句柄、网络连接等系统资源
  • 如打开的文件指针(FILE*)、网络套接字(socket)、数据库连接等,这些资源不会被默认析构函数自动释放,需在析构函数中显式关闭或释放(如 fclose()、close())。
    3.类使用了其他需要手动释放的资源
  • 例如动态申请的共享内存、互斥锁、图形库资源等,需在析构函数中调用对应的释放函数(如 munmap()、pthread_mutex_destroy())。
    4.类继承体系中存在虚函数
  • 若基类可能被继承,且派生类可能有动态资源,基类需定义虚析构函数(virtual ~ClassName()),否则删除基类指针时可能不会调用派生类的析构函数,导致资源泄漏。

不需要重写析构函数的情况:

  • 类成员均为基本类型(int、size_t 等)或标准库类型(std::string、std::vector 等),这些类型会自动管理资源,默认析构函数足够。
  • 类没有动态分配的资源或系统资源,仅通过成员对象的析构函数即可完成所有清理。

核心原则:当类需要手动管理资源(即资源的分配和释放不由编译器或标准库自动处理)时,必须重写析构函数。

2.使 User 类可进行复制构造。为此,请实现 User(const User& user) SMF

User::User(const User& user):_name(user._name),
                              _size(user._size),
                              _capacity(user._capacity),
                              _friends(nullptr)
{
  if (_capacity > 0) {
    _friends = new std::string[_capacity];
    for (size_t i = 0; i < _size; ++i) {
      _friends[i] = user._friends[i];
    }
  }
}

直接复制 _name(std::string 自带深拷贝,无需手动处理)、_size 和 _capacity 的值。
关键:为新对象的 _friends 重新分配一块内存(new std::string[_capacity]),而不是直接复制原对象的 _friends 指针(这是与浅拷贝的核心区别)。

3.实现 User& operator=(const User& user) SMF

User& User::operator=(const User& user){
  // 1. 自赋值检查:如果是自己赋值给自己,直接返回
  if(this == &user){
    return *this;
  }
   // 2. 释放当前对象的旧资源(避免内存泄漏)
   delete[] _friends;

  //3. 复制基本成员和容量信息
  _name = user._name;
  _size = user._size;
  _capacity = user._capacity;
  _friends = (_capacity > 0) ? new std::string[_capacity] : nullptr;
  for(size_t i = 0; i < _size; ++i){
    _friends[i] = user._friends[i];
  }
  return *this;
}

4.删除 User(User&& user) SMF/User& operator=(User&& user) SMF

  User(User&& user) = delete;
  User& operator=(User&& user) = delete;

Part 3: Always Be Friending

重载operator+=

User& User::operator+=(User& rhs){
  this->add_friend(rhs.get_name());
  rhs.add_friend(this->get_name());
}

重载 operator<

bool User::operator<(const User& rhs) const{
  return this->get_name() < rhs.get_name();
}
posted @ 2025-08-01 18:10  Civilight~Eterna  阅读(12)  评论(1)    收藏  举报