实验2 类和对象_基础编程1

任务1:

源代码:

t.h

 1 #pragma once
 2 
 3 #include <string>
 4 
 5 class T{
 6 public:
 7     T(int x=0,int y=0);
 8     T(const T &t);
 9     T(T &&t);
10     ~T();
11     
12     void adjust(int ratio);
13     void display() const;
14 
15 private:
16     int m1,m2;
17     
18 public:
19     static int get_cnt();
20     
21 public:
22     static const std::string doc;
23     static const int max_cnt;
24     
25 private:
26     static int cnt;
27     
28     friend void func();
29 };
30 
31 void func();

 

t.cpp

 1 #include"t.h"
 2 #include<iostream>
 3 #include<string>
 4 
 5 using std::cout;
 6 using std::endl;
 7 using std::string;
 8 
 9 const std::string T::doc{"a simple class sample"};
10 const int T::max_cnt=999;
11 int  T::cnt=0;
12 
13 T::T(int x,int y): m1{x},m2{y} {
14     ++cnt;
15      cout<<"T constructor called.\n";
16 }
17 
18 T::T(const T &t): m1{t.m1},m2{t.m2} {
19     ++cnt;
20     cout<<"T copy constructor called.\n";
21 }
22 
23 T::T(T &&t): m1{t.m1},m2{t.m2} {
24     ++cnt;
25     cout<<"T move constructor called.\n";
26 }
27 
28 T::~T() {
29     --cnt;
30     cout<<"T destructor called.\n";
31 }
32 
33 void T::adjust(int ratio) {
34     m1*=ratio;
35     m2*=ratio;
36 }
37 
38 void T::display() const {
39     cout<<"("<<m1<<","<<m2<<")";
40 }
41 
42 int T::get_cnt() {
43     return cnt;
44 }
45 
46 void func() {
47     T t5(42);
48     t5.m2 = 2049;
49     cout<<"t5 = "; t5.display(); cout<<endl;
50 }

 

task1.cpp

 1 #include"t.h"
 2 #include<iostream>
 3 
 4 using std::cout;
 5 using std::endl;
 6 
 7 void test();
 8 
 9 int main() {
10     test();
11     cout<<"\nmain: \n";
12     cout<<"T objects'current count: "<<T::get_cnt()<<endl;
13 }
14 
15 void test() {
16     cout<<"test class T: \n";
17     cout<<"T info: "<<T::doc<<endl;
18     cout<<"T objects'max count: "<<T::max_cnt<<endl;
19     cout<<"T objects'current count: "<<T::get_cnt() <<endl<<endl;
20     
21     T t1;
22     cout<<"t1 = "; t1.display(); cout<<endl;
23     
24     T t2(3,4);
25     cout<<"t2 = "; t2.display(); cout<<endl;
26     
27     T t3(t2);
28     t3.adjust(2);
29     cout<<"t3 = "; t3.display(); cout<<endl;
30     
31     T t4(std::move(t2));
32     cout<<"t3 = "; t4.display(); cout<<endl; 
33     
34     cout<<"T objects'current count: "<<T::get_cnt()<<endl;
35     
36     func(); 
37 }

 

问题1:

不能正确运行

截图:

可能原因:

C++ 中,必须在类头文件中的友元函数声明和在全局作用域中的函数声明之间理解作用域和可见性之间的差异。当在类中使用 friend声明一个函数(如 friend void func();),是在通知编译器这个函数是友元函数,因而能够访问该类的私有和保护成员。但这只提供了友元访问的能力。如果没有在全局作用域中声明函数,编译器在调用它时仍然不知道函数的原型,如果想要从类外部的其他地方调用该函数,需要在全局作用域中给出该函数的声明,以便编译器知道函数的参数和返回值类型。

在代码中,friend void func(); 声明了func 是一个友元函数,并允许这个函数访问类 X 的私有成员。然而,真正的函数原型 void func();仍然需要在全局作用域中明确声明,以便保证 func 在该作用域内可用,确保在其他源文件中调用这个函数时不会遇到链接错误。

 

问题2:

默认构造函数 (T(int x =0, int y =0))

功能:为类 T 创建对象时提供默认值。如果没有提供参数,则 x 和 y 被初始化为 0。此构造函数用于允许不带参数创建对象。

调用时机:当使用默认构造形式创建对象时,如 T t1; 或 T t2(3,4); 。

移动构造函数 (T(T &&t))

功能:通过转移(移动)另一个对象 t 的资源来初始化新对象,优化性能并减少内存的浪费。在移动构造中,原对象 t 的状态会被重置,防止其析构时意外释放资源。

调用时机:在使用 std::move 或在某些情况下如返回值优化时创建对象时,例如:T t4(std::move(t2)); 。

析构函数析构函数 (~T())

功能:用于清理对象在生命周期内分配的资源,负责释放动态分配的内存、关闭打开的文件、释放网络连接等。

调用时机:当对象的作用域结束或对象被显式删除时(例如:当一个方法结束、局部对象超出作用域时或通过 delete关键字释放对象时)。

 

问题3:

不能,所有的静态成员变量(包括静态常量和静态计数器)都应在一个源文件中定义和初始化,即上面的 t.cpp。

 

运行成功截图:

 

任务2:

Complex.h

 1 #include<iostream>
 2 #include<string>
 3 
 4 class Complex{
 5 public:
 6     Complex(double r=0.0,double i=0.0);
 7     Complex(const Complex &c);
 8     
 9     double get_real() const;
10     double get_imag() const;
11     void add(const Complex& c);
12 
13 private:
14     double real;
15     double imag;
16     
17 public:
18     static const std::string doc;
19         
20     friend Complex add(const Complex& c1,const Complex& c2);
21     friend bool is_equal(const Complex& c1,const Complex& c2);
22     friend bool is_not_equal(const Complex& c1,const Complex& c2);
23     friend double abs(const Complex& c);
24     friend void output(const Complex& c);
25     
26 }; 

 

Complex.cpp

 1 #include"Complex.h"
 2 #include<iostream>
 3 #include<string>
 4 #include<cmath>
 5 
 6 const std::string Complex::doc{"a simplified complex class"};
 7 
 8 Complex::Complex(double r,double i): real{r},imag{i} {}
 9 
10 Complex::Complex(const Complex &c): real{c.real},imag{c.imag} {}
11 
12 double Complex::get_real() const {
13     return real;
14 }
15 
16 double Complex::get_imag() const  {
17     return imag;
18 }
19 
20 void Complex::add(const Complex& c) {
21     real+=c.real;
22     imag+=c.imag;
23 }
24 
25 Complex add(const Complex& c1,const Complex& c2) {
26      return Complex(c1.real + c2.real, c1.imag + c2.imag);
27 }
28 
29 bool is_equal(const Complex& c1,const Complex& c2) {
30     if(c1.real==c2.real&&c1.imag==c2.imag)
31     return true;
32     else
33     return false;
34 }
35 
36 bool is_not_equal(const Complex& c1,const Complex& c2) {
37     if((c1.real!=c2.real||c1.imag!=c2.imag))
38     return true;
39     else
40     return false;
41 }
42 
43 double abs(const Complex& c) {
44     return sqrt(c.real*c.real+c.imag*c.imag);
45 }
46 
47 void output(const Complex& c) {
48     std::cout<<c.real;
49     if(c.imag>=0)
50     std::cout<<" + ";
51     std::cout<<c.imag<<"i";
52 }

 

task2.cpp
 1 #include "Complex.h"
 2 #include <iostream>
 3 
 4 using std::cout;
 5 using std::endl;
 6 using std::boolalpha;
 7 
 8 void test() {
 9     cout << "类成员测试: " << endl;
10     cout << Complex::doc << endl;
11 
12     cout << endl;
13 
14     cout << "Complex对象测试: " << endl;
15     Complex c1;
16     Complex c2(3, -4);
17     const Complex c3(3.5);
18     Complex c4(c3);
19 
20     cout << "c1 = "; output(c1); cout << endl;
21     cout << "c2 = "; output(c2); cout << endl;
22     cout << "c3 = "; output(c3); cout << endl;
23     cout << "c4 = "; output(c4); cout << endl;
24     cout << "c4.real = " << c4.get_real() << ", c4.imag = " << c4.get_imag() << endl;
25 
26     cout << endl;
27 
28     cout << "复数运算测试: " << endl;
29     cout << "abs(c2) = " << abs(c2) << endl;
30     c1.add(c2);
31     cout << "c1 += c2, c1 = "; output(c1); cout << endl;
32     cout << boolalpha;
33     cout << "c1 == c2 : " << is_equal(c1, c2) << endl;
34     cout << "c1 != c3 : " << is_not_equal(c1, c3) << endl;
35     c4 = add(c2, c3);
36     cout << "c4 = c2 + c3, c4 = "; output(c4); cout << endl;
37 }
38 
39 int main() {
40     test();
41 }

 

运行成功截图:

 

任务3:

task3.cpp

 1 #include<iostream>
 2 #include<complex>
 3 
 4 using std::cout;
 5 using std::endl;
 6 using std::boolalpha;
 7 using std::complex;
 8 
 9 void test() {
10     cout<<"标准库模板类complex测试:"<<endl;
11     complex<double> c1;
12     complex<double> c2(3,-4);
13     const complex<double> c3(3.5);
14     complex<double> c4(c3);
15     
16     cout<<"c1 = "<<c1<<endl;
17     cout<<"c2 = "<<c2<<endl;
18     cout<<"c3 = "<<c3<<endl;
19     cout<<"c4 = "<<c4<<endl;
20     cout<<"c4.real = "<<c4.real()<<",c4.imag = "<<c4.imag()<<endl;
21     cout<<endl;
22     
23     cout<<"复数运算测试:"<<endl;
24     cout<<"abs(c2) = "<<abs(c2)<<endl;
25     c1+=c2;
26     cout<<"c1+=c2,c1 = "<<c1<<endl;
27     cout<<boolalpha;
28     cout<<"c1==c2: "<<(c1==c2)<<endl;
29     cout<<"c1!=c3: "<<(c1!=c4)<<endl;
30     c4=c2+c3;
31     cout<<"c4 = c2 + c3, c4 = "<<c4<<endl;    
32 }
33 
34 int main() {
35     test();
36 }

 

运行结果截图:

 

对比任务2:

1.对比差异

比较判断两个复数是否相同,标准库中的接口为 (c1==c2) 和 (c1!=c4) 取代了 is_equal(c1, c2) 和 is_not_equal(c1, c3)

将另一个复数加到一复数上,标准库中的接口为c1+=c2;取代了实验二中的c1.add(c2)

标准库中输出直接为cout<<c1;取代了实验二中的output()函数

求两复数之和 c4=c2+c3 取代了 c4 = add(c2, c3);
2.启发
标准库的接口如 c4 = c2 + c3使得复数的加法更符合直观的数学表达,提升了可读性。而自定义实现的 add(c2, c3) 则显得不够自然。

标准库允许通过 std::cout << c1直接输出复数,而自定义实现的 output() 方法,降低了代码的可读性和灵活性。

标准库通过运算符重载(如 c1 == c2 和 c1 != c2)使得复数相等比较的语法更为简便直观,而自定义实现使用函数调用(如 is_equal(c1, c2))显得冗长。

设计时应追求简洁、高效的接口,尽量减少冗长的函数调用,提升代码流畅性。
 
任务4:
Fraction.h
 1 #pragma once
 2 #include<iostream>
 3 
 4 class Fraction{
 5 public:
 6     Fraction(int up, int down=1);
 7     Fraction(const Fraction &f);
 8     
 9     int get_up() const;
10     int get_down() const;
11     Fraction negative() const;
12     void simplify();
13     int gcd(int a,int b);
14     
15     static const std::string doc;
16 private:
17     int up,down;    
18         
19     friend void output(const Fraction& f1);
20     friend Fraction add(const Fraction& f1,const Fraction& f2);
21     friend Fraction sub(const Fraction& f1,const Fraction& f2);
22     friend Fraction mul(const Fraction& f1,const Fraction& f2);
23     friend Fraction div(const Fraction& f1,const Fraction& f2);
24 };
25 
26 void output(const Fraction &f);
27 Fraction add(const Fraction &f1,const Fraction &f2);
28 Fraction sub(const Fraction &f1,const Fraction &f2);
29 Fraction mul(const Fraction &f1,const Fraction &f2);
30 Fraction div(const Fraction &f1,const Fraction &f2);

 

Fraction.cpp
 1 #include"Fraction.h"
 2 #include<string>
 3 #include<iostream>
 4 
 5 using namespace std;
 6 
 7 const string Fraction::doc{"Fraction类 v 0.01版.\n目前仅支持分数对象的构造、输出、加/减/乘/除运算."};
 8 
 9 Fraction::Fraction(const Fraction& f): up{f.up},down{f.down} {}
10 
11 int Fraction::gcd(int a,int b){
12     while(b!=0){
13         int r=a%b;
14         a=b;
15         b=r;
16     }
17     return a;
18 }
19 
20 void Fraction::simplify(){
21     if(down<0)
22     {
23         up=-up;
24         down=-down;
25     }
26     int k=gcd(up,down);
27     up=up/k;
28     down=down/k;
29 }
30 
31 Fraction::Fraction(int up,int down):up{up},down{down}{
32     if (down == 0) {
33         cout<<"分母不能为0"<<endl;
34     }
35     simplify();}
36 
37 int Fraction::get_up()const{
38     return up;
39 }
40 int Fraction::get_down()const{
41     return down;
42 }
43 
44 
45 Fraction Fraction::negative() const{
46     return Fraction(-up,down);
47 }
48 
49 void output(const Fraction &f) {
50     if(f.get_down()==0)
51     return;
52     else if(f.get_up()==0)
53     cout<<"0";
54     else if(f.get_down()==1)
55     cout<<f.get_up();
56     else if(f.get_up()>0&&f.get_down()>0)
57     cout<<f.get_up()<<"/"<<f.get_down();
58     else if(f.get_up()<0&&f.get_down()<0)
59     cout<<-f.get_up()<<"/"<<-f.get_down();
60     else if(f.get_up()>0&&f.get_down()<0)
61     cout<<-f.get_up()<<"/"<<-f.get_down();
62     else
63     cout<<f.get_up()<<"/"<<f.get_down(); 
64 }
65 
66 
67 Fraction add(const Fraction& f1,const Fraction& f2){
68     return Fraction(f1.get_up()*f2.get_down()+f2.get_up()*f1.get_down(),f1.get_down()*f2.get_down());
69 }
70 Fraction sub(const Fraction& f1,const Fraction& f2){
71     return Fraction(f1.get_up()*f2.get_down()-f2.get_up()*f1.get_down(),f1.get_down()*f2.get_down());
72 }
73 Fraction mul(const Fraction& f1,const Fraction& f2){
74     return Fraction(f1.get_up()*f2.get_up(),f1.get_down()*f2.get_down());
75 }
76 Fraction div(const Fraction& f1,const Fraction& f2){
77     return Fraction(f1.get_up()*f2.get_down(),f1.get_down()*f2.get_up());
78 }

 

task4.cpp
#include "Fraction.h"
#include <iostream>

using std::cout;
using std::endl;


void test1() {
    cout << "Fraction类测试: " << endl;
    cout << Fraction::doc << endl << endl;

    Fraction f1(5);
    Fraction f2(3, -4), f3(-18, 12);
    Fraction f4(f3);
    cout << "f1 = "; output(f1); cout << endl;
    cout << "f2 = "; output(f2); cout << endl;
    cout << "f3 = "; output(f3); cout << endl;
    cout << "f4 = "; output(f4); cout << endl;

    Fraction f5(f4.negative());
    cout << "f5 = "; output(f5); cout << endl;
    cout << "f5.get_up() = " << f5.get_up() << ", f5.get_down() = " << f5.get_down() << endl;

    cout << "f1 + f2 = "; output(add(f1, f2)); cout << endl;
    cout << "f1 - f2 = "; output(sub(f1, f2)); cout << endl;
    cout << "f1 * f2 = "; output(mul(f1, f2)); cout << endl;
    cout << "f1 / f2 = "; output(div(f1, f2)); cout << endl;
    cout << "f4 + f5 = "; output(add(f4, f5)); cout << endl;
}

void test2() {
    Fraction f6(42, 55), f7(0, 3);
    cout << "f6 = "; output(f6); cout << endl;
    cout << "f7 = "; output(f7); cout << endl;
    cout << "f6 / f7 = "; output(div(f6, f7)); cout << endl;
}

int main() {
    cout << "测试1: Fraction类基础功能测试\n";
    test1();

    cout << "\n测试2: 分母为0测试: \n";
    test2();
}

 

运行结果截图:

 

任务5:
account.h
 1 #pragma once
 2 #include<iostream>
 3 
 4 class SavingAccount {
 5 private:
 6     int id;
 7     double balance;
 8     double rate;
 9     int lastDate;
10     double accumulation;
11     static double total;
12     void record(int date,double amount);
13     double accumulate(int date) const {
14         return accumulation+balance*(date-lastDate);    
15     }    
16 public:
17     SavingAccount(int date,int id,double rate);
18     int getID() const {return id;}
19     double getBalance() const {return balance;}
20     double getRate() const {return rate;}
21     static double getTotal() {return total;}
22     void deposit(int date,double amount);
23     void withdraw(int date,double amount);
24     void settle(int date);
25     void show() const; 
26 };

 

account.cpp
 1 #include"account.h"
 2 #include<iostream>
 3 #include<cmath>
 4 
 5 using namespace std;
 6 
 7 double SavingAccount::total = 0;
 8 SavingAccount::SavingAccount(int date,int id,double rate): id(id),balance(0),rate(rate),lastDate(date),accumulation(0) {
 9     cout<<date<<"\t#"<<id<<"is created"<<endl;
10 }
11 
12 void SavingAccount::record(int date,double amount) {
13     accumulation = accumulate(date);
14     lastDate = date;
15     amount = floor(amount*100+0.5)/100;
16     balance+=amount;
17     total+=amount;
18     cout<<date<<"\t#"<<id<<"\t"<<amount<<"\t"<<balance<<endl;
19 }
20 
21 void SavingAccount::deposit(int date,double amount) {
22     record(date,amount); 
23 }
24 
25 void SavingAccount::withdraw(int date,double amount) {
26     if(amount>getBalance())
27         cout<<"Error:not enough money"<<endl;
28     else
29         record(date,-amount);
30 }
31 
32 void SavingAccount::settle(int date) {
33     double interst = accumulate(date)*rate/365;
34     if(interst != 0)
35         record(date,interst);
36     accumulation = 0;
37 }
38 
39 void SavingAccount::show() const {
40     cout<<"#"<<id<<"\tBalance:"<<balance;
41 }

 

task5.cpp
 1 #include"account.h"
 2 #include<iostream>
 3 using namespace std;
 4 int main() {
 5     SavingAccount sa0(1, 21325302, 0.015);
 6     SavingAccount sa1(1, 58320212, 0.015);
 7     sa0.deposit(5, 5000);
 8     sa1.deposit(25, 10000);
 9     sa0.deposit(45, 5500);
10     sa1.withdraw(60, 4000);
11     sa0.settle(90);
12     sa1.settle(90);
13     sa0.show(); cout << endl;
14     sa1.show(); cout << endl;
15     cout << "Total: " << SavingAccount::getTotal() << endl;
16     return 0;
17 
18 }

 

运行截图:

 

思考:
SavingAccount 类的接口和内部计算模块基本上是合理的,能够实现储蓄账户的基本功能,如存款、取款、结算和显示账户状态。
在此基础上,对输入参数的有效性检查在存款、取款和结算操作中,应该检查输入参数的有效性。
可以考虑将结算逻辑(利息计算)与账户操作分开,或者将结算逻辑抽象到一个单独的服务类中。这样可以简化 SavingAccount 类的复杂度。
 
实验总结:
代码组织我们将类的声明与实现分离,使用头文件(.h)和实现文件(.cpp)的形式,增强了代码的可管理性和可读性。这种组织方式使得项目结构更加清晰。
通过封装与接口实现了封装的核心概念,通过将数据成员(如 balance 和 rate)设为 private,并为其提供公有的操作方法(如 depositwithdraw 和 settle)。这种方式使得外部对象无法直接修改内部状态,从而保护了数据的完整性。熟悉了C++的类定义语法,包括构造函数、析构函数的使用。实践了访问权限控制,确保了类的成员在适当的封装级别下进行访问。
posted @ 2024-10-29 16:54  F&WJ  阅读(12)  评论(0编辑  收藏  举报