面向对象程序设计-06-构造与析构

声明

题解包含以下内容:

  • (相对)高级的 C++ 模板及语法技巧
  • 仅适用于 C++20 标准的代码
  • 强烈的个人代码风格和 Modern C++ 范式追求
  • 泛滥的标准库函数

换句话说就是(相较于其他公开题解)更低的查重率和更高的使用门槛;请酌情使用。

【id:31】【20分】A. Point(类与构造)

题目描述

下面是一个平面上的点的类定义,请在类外实现它的所有方法,并生成点测试它。

lab6-A-1

输入

测试数据的组数 t

第一组测试数据点p1的x坐标 第一组测试数据点p1的y坐标 第一组测试数据点p2的x坐标 第一组测试数据点p2的y坐标

输出

输出p1到p2的距离,保留两位小数。详情参考输出样例。

在C++中,输出指定精度的参考代码如下:

#include <iostream>

#include <iomanip> //必须包含这个头文件

using namespace std;

void main( )

{ double a =3.14;

  cout<<fixed<<setprecision(3)<<a<<endl;  //输出小数点后3位

}

样例

输入样例1输出样例1
2
1 2 3 4
-1 0.5 -2 5
Distance of Point(1.00,2.00) to Point(3.00,4.00) is 2.83
Distance of Point(-1.00,0.50) to Point(-2.00,5.00) is 4.61

Answer

#include <bits/stdc++.h>
using namespace std;

class Point {
  double x, y;

public:
  Point();
  Point( double x_, double y_ );
  double getX() const;
  double getY() const;
  void setX( double x_ ) noexcept;
  void setY( double y_ ) noexcept;
  double distanceToAnotherPoint( const Point& pt ) const;

  friend istream& operator>>( istream& is, Point& pt ) {
    return is >> pt.x >> pt.y;
  }
};
Point::Point() : Point({}, {}) {}
Point::Point( double x_, double y_ )
  : x {x_}, y {y_} {}
double Point::getX() const { return x; }
double Point::getY() const { return y; }
void Point::setX( double x_ ) noexcept { x = x_; }
void Point::setY( double y_ ) noexcept { y = y_; }
double Point::distanceToAnotherPoint( const Point& pt ) const
{
  return sqrt( pow( abs( x - pt.x ), 2 ) + pow( abs( y - pt.y ), 2 ) );
}

ostream& operator<<( ostream& os, const Point& pt )
{
  return os << fixed << setprecision(2) << '(' << pt.getX() << ',' << pt.getY() << ')';
}

int main()
{
  size_t t = 0;
  cin >> t;
  while (t--) {
    Point pt1, pt2;
    cin >> pt1 >> pt2;
    cout << "Distance of Point"sv << pt1
      << " to Point"sv << pt2
      << " is "sv << pt1.distanceToAnotherPoint( pt2 ) << endl;
  }

  return 0;
}

【id:32】【20分】B. Date(类与构造)

题目描述

下面是一个日期类的定义,请在类外实现其所有的方法,并在主函数中生成对象测试之。

lab6-B-1

注意,在判断明天日期时,要加入跨月、跨年、闰年的判断

例如9.月30日的明天是10月1日,12月31日的明天是第二年的1月1日

2月28日的明天要区分是否闰年,闰年则是2月29日,非闰年则是3月1日

输入

测试数据的组数t

第一组测试数据的年 月 日

要求第一个日期的年月日初始化采用构造函数,第二个日期的年月日初始化采用setDate方法,第三个日期又采用构造函数,第四个日期又采用setDate方法,以此类推。

输出

输出今天的日期

输出明天的日期

样例

输入样例1输出样例1
4
2012 1 3
2012 2 28
2012 3 31
2012 4 30
Today is 2012/01/03
Tomorrow is 2012/01/04
Today is 2012/02/28
Tomorrow is 2012/02/29
Today is 2012/03/31
Tomorrow is 2012/04/01
Today is 2012/04/30
Tomorrow is 2012/05/01

提示

C++中设置填充字符的代码参考如下:

cout << setfill(‘0’) << setw(2) << month; //设置宽度为2,前面补’0’

需要头文件#include <iomanip>

Answer

#include <bits/stdc++.h>
using namespace std;

class Date {
  int year, month, day;

  static constexpr bitset<12> big_month = 0b10'10'11'01'01'01;

public:
  Date();
  Date( int yy, int mm, int dd );
  int getYear() const;
  int getMonth() const;
  int getDay() const;
  void setDate( int yy, int mm, int dd ) noexcept;
  void print() const;
  void addOneDay() noexcept;

  friend istream& operator>>( istream& is, Date& lhs ) {
    return is >> lhs.year >> lhs.month >> lhs.day;
  }
};
Date::Date() : Date({}, {}, {}) {}
Date::Date( int yy, int mm, int dd )
  : year { yy }, month { mm }, day { dd } {}
int Date::getYear() const { return year; }
int Date::getMonth() const { return month; }
int Date::getDay() const { return day; }
void Date::print() const {
  cout << getYear() << '/' <<
    setfill( '0' ) << setw( 2 ) << getMonth() << '/' <<
    setfill('0') << setw(2) << getDay();
}
void Date::setDate( int yy, int mm, int dd ) noexcept
{
  year = yy; month = mm; day = dd;
}
void Date::addOneDay() noexcept {
  day += 1;

  if ( const int day_upper =
         (big_month[month - 1] ? 31
         : (month != 2 ? 30
         : ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 29
         : 28));
    day > day_upper ) {
    ++month;
    day -= day_upper;
  }
  if ( month >= 12 ) {
    ++year;
    month -= 12;
  }
}

ostream& operator<<( ostream& os, const Date& lhs )
{
  return os << lhs.getYear() << '/' <<
    setfill( '0' ) << setw( 2 ) << lhs.getMonth() << '/' <<
    setfill( '0' ) << setw( 2 ) << lhs.getDay();
}

int main()
{
  size_t t = 0;
  cin >> t;
  for ( size_t i = 0; i < t; ++i ) {
    Date dt; cin >> dt;
    cout << "Today is " << dt << endl;
    dt.addOneDay();
    cout << "Tomorrow is " << dt << endl;
  }
}

【id:33】【20分】C. 分数类(类与构造)

题目描述

完成下列分数类的实现:

class CFraction
{
private:
     int fz, fm;
public:
     CFraction(int fz_val, int fm_val) ;
     CFraction add(const CFraction &r);
     CFraction sub(const CFraction &r);
     CFraction mul(const CFraction &r);
     CFraction div(const CFraction &r);
     int getGCD();   // 求对象的分子和分母的最大公约数
     void print();
};

求两数a、b的最大公约数可采用辗转相除法,又称欧几里得算法,其步骤为:

  1. 交换a, b使a > b;
  2. 用a除b得到余数r,若r=0,则b为最大公约数,退出.
  3. 若r不为0,则用b代替a, r代替b,此时a,b都比上一次的小,问题规模缩小了;
  4. 继续第2步。

注意:如果分母是1的话,也按“分子/1”的方式输出。

输入

测试数据的组数 t

第一组第一个分数

第一组第二个分数

第二组第一个分数

第二组第二个分数

输出

第一组两个分数的和

第一组两个分数的差

第一组两个分数的积

第一组两个分数的商

第二组两个分数的和

第二组两个分数的差

第二组两个分数的积

第二组两个分数的商

样例

输入样例1输出样例1
3
1/2
2/3
3/4
5/8
21/23
8/13
7/6
-1/6
1/3
3/4

11/8
1/8
15/32
6/5

457/299
89/299
168/299
273/184

Answer

#include <bits/stdc++.h>
using namespace std;

class CFraction {
  int fz, fm;

  void reduction() {
    if ( int gcd_num = gcd( abs( fz ), abs( fm ) );
         gcd_num >= 1 ) {
      fz /= gcd_num;
      fm /= gcd_num;
    }
    // 保持负号在分子上
    if ( fm < 0 && fz > 0 ) {
      fz *= -1;
      fm = abs(fm);
    } else if ( fz < 0 && fm < 0 ) {
      fz = abs( fz );
      fm = abs( fm );
    }
  }

public:
  CFraction( int fz_val, int fm_val )
    : fz { fz_val }, fm { fm_val } {
    reduction();
  }
  CFraction() : CFraction( {}, {} ) {}
  CFraction add( const CFraction& r ) {
    //int lcm = fm == r.fm ? 1 : fm * r.fm / gcd( fm, r.fm );
    return CFraction(fz * r.fm + r.fz * fm, fm * r.fm);
  }
  CFraction sub( const CFraction& r ) {
    //int lcm = fm == r.fm ? 1 : fm * r.fm / gcd(fm, r.fm);
    return CFraction(fz * r.fm - r.fz * fm, fm * r.fm);
  }
  CFraction mul( const CFraction& r ) {
    return CFraction(fz * r.fz, fm * r.fm);
  }
  CFraction div( const CFraction& r ) {
    return CFraction( fz * r.fm, fm * r.fz);
  }
  int getGCD() {
    return gcd( fz, fm );
  } // 求对象的分子和分母的最大公约数
  void print() {
    cout << fz << '/' << fm;
  }
  friend ostream& operator<<( ostream& os, const CFraction& lhs ) {
    return (lhs.fz != 0 ? (os << lhs.fz << '/' << lhs.fm) : (os << 0));
  }
  friend istream& operator>>( istream& is, CFraction& lhs ) {
    char tmp; return is >> lhs.fz >> tmp >> lhs.fm;
  }
};


int main() {
  int t; cin >> t;
  while ( t-- ) {
    CFraction num1, num2;
    cin >> num1 >> num2;
    cout << num1.add( num2 ) << endl
      << num1.sub( num2 ) << endl
      << num1.mul( num2 ) << endl
      << num1.div( num2 ) << endl << endl;
  }
}

【id:34】【20分】D. Point_Array(类+构造+对象数组)

题目描述

lab6-D-1

上面是我们曾经练习过的一个习题,请在原来代码的基础上作以下修改:

1、增加自写的析构函数;

2、将getDisTo方法的参数修改为getDisTo(const Point &p);

3、根据输出的内容修改相应的构造函数。

然后在主函数中根据用户输入的数目建立Point数组,求出数组内距离最大的两个点之间的距离值。

输入

测试数据的组数 t

第一组点的个数

第一个点的 x 坐标 y坐标

第二个点的 x坐标 y坐标

输出

输出每组中距离最大的两个点以及其距离(存在多个距离都是最大值的情况下,输出下标排序最前的点组合。比如如果p[0]和p[9]、p[4]和p[5]之间的距离都是最大值,那么前一个是答案,因为p[0]排序最前)

在C++中,输出指定精度的参考代码如下:

#include <iostream>

#include <iomanip> //必须包含这个头文件

using namespace std;

void main( )

{ double a =3.141596;

  cout<<fixed<<setprecision(3)<<a<<endl;  //输出小数点后3位

样例

输入样例1输出样例1
2
4
0 0
5 0
5 5
2 10
3
-1 -8
0 9
5 0
Constructor.
Constructor.
Constructor.
Constructor.
The longest distance is 10.44,between p[1] and p[3].
Distructor.
Distructor.
Distructor.
Distructor.
Constructor.
Constructor.
Constructor.
Constructor.
The longest distance is 17.03,between p[0] and p[1].
Distructor.
Distructor.
Distructor.
Distructor.

Answer

#include <bits/stdc++.h>
using namespace std;

class Point {
  double x, y;

public:
  Point();
  Point( double x_, double y_ );
  ~Point();
  double getX() const;
  double getY() const;
  void setX( double x_ ) noexcept;
  void setY( double y_ ) noexcept;
  double getDisTo( const Point& pt ) const;

  friend istream& operator>>( istream& is, Point& pt ) {
    return is >> pt.x >> pt.y;
  }
};
Point::Point() : Point({}, {}) {}
Point::Point( double x_, double y_ )
  : x { x_ }, y { y_ } {
  cout << "Constructor."sv << endl;
}
Point::~Point() {
  cout << "Distructor."sv << endl;
}
double Point::getX() const { return x; }
double Point::getY() const { return y; }
void Point::setX( double x_ ) noexcept { x = x_; }
void Point::setY( double y_ ) noexcept { y = y_; }
double Point::getDisTo( const Point& pt ) const
{
  return sqrt( pow( abs( x - pt.x ), 2 ) + pow( abs( y - pt.y ), 2 ) );
}

int main()
{
  size_t t; cin >> t;
  while ( t-- ) {
    size_t scale = 0; cin >> scale;
    auto arr = vector<Point>( scale );
    for ( auto& e : arr )
      cin >> e;

    double dist {};
    size_t px {}, py {};
    for ( size_t i = 0; i < scale; ++i ) {
      for ( size_t ii = 0; ii < scale; ++ii ) {
        if (ii == i) continue;
        if ( const double distan = arr[i].getDisTo( arr[ii] );
             distan > dist ) {
          dist = distan;
          px = i; py = ii;
        }
      }
    }
    cout << "The longest distance is "
      << fixed << setprecision( 2 ) << dist
      << ",between p[" << px << "] and p[" << py << "]." << endl;
  }
}

【id:35】【20分】E. Stack(类与构造)

题目描述

lab6-E-1

上面是栈类的定义,栈是一种具有先进后出特点的线性表,请根据注释,完成类中所有方法的实现,并在主函数中测试之。

堆栈类的说明如下:

  1. 堆栈的数据实际上是保存在数组a中,而a开始是一个指针,在初始化时,根据实际需求将a动态创建为数组,数组长度根据构造函数的参数决定。
  2. size实际上就是数组的长度,当使用无参构造则size为10,当使用有参构造则size为s、
  3. top表示数组下标,也表示数组中下一个存放数据的空白位置。
  4. push操作表示堆栈的数组存放一个数据,例如一开始数组为空,则top为0,当有数据要入栈时,把数据存放在a[top]的位置,然后top加1指向下一个空白位置、数据进栈只能从栈顶进。
  5. pop操作表示一个数据要出栈,数据出栈只能从栈顶出,先把top减1指向栈顶数据,然后把数据返回。
  6. 判断堆栈空的条件是top是否等于0,判断堆栈满的条件是top是否等于size

输入

测试数据的组数 t

第一个栈的大小

第一个栈的元素列表,将该列表的元素依次进栈

输出

将栈元素依次出栈

样例

输入样例1输出样例1
2
5
1 2 3 4 5
7
-1 2 8 0 -3 1 3
Constructor.
5 4 3 2 1
Destructor.
Constructor.
3 1 -3 0 8 2 -1
Destructor.

Answer

#include <bits/stdc++.h>
using namespace std;

class CStack {
  int* a;
  int size;
  int top;

public:
  CStack() : a { nullptr }, size {}, top { -1 } {
    cout << "Constructor." << endl;
  }
  CStack( int s ) : CStack() {
    size = s;
    a = new int[size] {};
    top = -1;
  }
  int get( int index ) {
    return a[index];
  }
  void push( int n ) {
    a[++top] = n;
  }
  int isEmpty() {
    return top == -1;
  }
  int isFull() {
    return top == size - 1;
  }
  int pop() {
    return a[top--];
  }
  ~CStack() {
    delete[] a;
    cout << "Destructor." << endl;
  }
};

int main()
{
  size_t t = 0;
  cin >> t;
  while ( t-- ) {
    int stk_size; cin >> stk_size;
    auto mystk = CStack( stk_size );
    for ( size_t i = 0; i < stk_size; ++i ) {
      int tmp; cin >> tmp;
      mystk.push( tmp );
    }
    for ( int i = 0; i < stk_size; ++i ) {
      cout << mystk.pop();
      cout << (i < stk_size - 1 ? ' ' : '\n');
    }
  }
}
  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值