面向对象程序设计——Visual C++

第7章 面向对象程序设计 思考题

1、大型程序的复杂性体现在哪些地方?它们会给编程人员带来哪些障碍?

大型程序的复杂性体现在以下几个方面:

  1. 代码量庞大,难以维护和修改;
  2. 模块之间的依赖关系复杂,修改一个模块可能会影响到其他模块;
  3. 数据结构和算法的选择和设计需要考虑更多的因素,如效率、可扩展性、可维护性等;
  4. 测试和调试难度大,需要花费更多的时间和精力。

这些障碍会给编程人员带来更高的工作量和压力,需要更高的技能和经验来应对。

2、面向对象思想产生的原因是什么?为什么它会成为主要的编程思想?

面向对象思想产生的原因是为了更好地解决软件开发中的复杂性问题。它将数据和操作封装在一起,形成对象,通过对象之间的交互来实现程序的功能。面向对象思想具有以下优点:

  1. 提高了代码的可重用性和可维护性;
  2. 降低了模块之间的耦合度,使得程序更加灵活和可扩展;
  3. 更加符合人类的思维方式,易于理解和使用。

由于面向对象思想具有这些优点,因此成为了主要的编程思想之一。

3、什么是面向对象程序设计?与结构化程序设计相比,它有哪些优越性?

面向对象程序设计是一种以对象为中心的程序设计方法。它将数据和操作封装在一起,形成对象,通过对象之间的交互来实现程序的功能。与结构化程序设计相比,面向对象程序设计具有以下优越性:

  1. 更加符合人类的思维方式,易于理解和使用;
  2. 提高了代码的可重用性和可维护性;
  3. 降低了模块之间的耦合度,使得程序更加灵活和可扩展;
  4. 支持继承和多态等特性,使得程序设计更加灵活和高效。

总的来说,面向对象程序设计是一种更加高级和先进的程序设计方法,可以更好地解决软件开发中的复杂性问题。

第8章 面向对象的基本概念及其特点 思考题

1、什么是对象?在计算机系统中对象有哪些特点?

对象是面向对象程序设计中的基本概念,是一个封装了数据和操作的实体。对象具有以下特点:

  1. 具有状态,即对象的属性或数据;
  2. 具有行为,即对象的操作或方法;
  3. 具有标识,即对象的唯一标识符或引用。

在计算机系统中,对象通常是由类定义的,类是一种模板或蓝图,用于创建对象。对象可以通过类的构造函数创建,通过类的析构函数销毁。对象还可以通过引用或指针进行访问和操作。对象的特点使得它们可以更好地封装数据和操作,提高程序的可重用性和可维护性。

2、类和对象之间有什么样的关系?

类是对象的模板,对象是类的实例。
类定义了对象的属性和行为,而对象则具体化了类的属性和行为。
类和对象之间的关系可以理解为蓝图和建筑物之间的关系,蓝图定义了建筑物的结构和特征,而建筑物则是蓝图的具体实现。
在程序中,类定义了数据类型和函数,而对象则是这些数据类型和函数的实例。通过创建对象,我们可以使用类中定义的属性和方法来完成特定的任务。

3、一个对象有哪些类型的行为?它们各有什么作用?

一个对象可以有以下类型的行为:

  1. 属性:对象可以拥有一些属性,这些属性可以是基本数据类型,也可以是其他对象类型。属性可以描述对象的状态或特征。
  2. 方法:对象可以拥有一些方法,这些方法可以是对象自己定义的,也可以是从类中继承的。方法可以描述对象的行为或操作。
  3. 构造函数:对象可以拥有一个构造函数,用于初始化对象的属性和方法。
  4. 拷贝构造函数:对象可以拥有一个拷贝构造函数,用于创建一个新的对象,该对象与原对象具有相同的属性和方法。
  5. 赋值运算符:对象可以拥有一个赋值运算符,用于将一个对象的属性和方法赋值给另一个对象。

这些行为各有不同的作用,属性可以描述对象的状态或特征,方法可以描述对象的行为或操作,构造函数用于初始化对象的属性和方法,拷贝构造函数用于创建一个新的对象,赋值运算符用于将一个对象的属性和方法赋值给另一个对象。这些行为共同构成了对象的行为特征,使得对象能够完成特定的任务。

4、面向对象系统具有哪些特性?

面向对象系统具有以下特性:

  1. 封装性:将数据和方法封装在一个类中,只对外暴露必要的接口,隐藏内部实现细节,提高安全性和可维护性。
  2. 继承性:通过继承机制,子类可以继承父类的属性和方法,避免重复编写代码,提高代码复用性和可扩展性。
  3. 多态性:同一种行为可以有不同的表现形式,通过虚函数和多态机制,可以实现运行时动态绑定,提高代码灵活性和可扩展性。
  4. 抽象性:通过抽象类和接口,将共性抽象出来,提高代码的可读性和可维护性。
  5. 类型的静态绑定:在编译时确定对象的类型,提高代码的效率和安全性。
  6. 类型的动态绑定:在运行时确定对象的类型,实现多态性,提高代码的灵活性和可扩展性。

这些特性共同构成了面向对象系统的基础,使得程序设计更加灵活、可扩展和易于维护。

5、什么是面向对象系统的封装性?什么是面向对象的继承性?什么是面向对象系统的多态性?

  1. 面向对象系统的封装性是指将数据和方法封装在一个类中,只对外暴露必要的接口,隐藏内部实现细节,提高安全性和可维护性。封装性可以保护数据不被外部直接访问和修改,同时也可以隐藏实现细节,使得代码更加清晰和易于维护。
  2. 面向对象的继承性是指通过继承机制,子类可以继承父类的属性和方法,避免重复编写代码,提高代码复用性和可扩展性。继承性可以使得代码更加简洁和易于维护,同时也可以实现代码的层次化和模块化。
  3. 面向对象系统的多态性是指同一种行为可以有不同的表现形式,通过虚函数和多态机制,可以实现运行时动态绑定,提高代码灵活性和可扩展性。多态性可以使得代码更加灵活和易于扩展,同时也可以实现代码的可读性和可维护性。

第9章 类及对象的封装性 思考题

1、什么是类?类的一般形式是什么?

类是一种用户自定义的数据类型,用于描述具有相同属性和行为的对象集合。类的一般形式包括类名、属性和方法。类名用于标识类的名称,属性用于描述类的状态或特征,方法用于描述类的行为或操作。类的定义通常包括在头文件中,可以在程序中创建对象并使用类中定义的属性和方法。例如,以下是一个简单的类的定义:

class Person {
private:
    string name;
    int age;
public:
    void setName(string n) {
        name = n;
    }
    void setAge(int a) {
        age = a;
    }
    string getName() {
        return name;
    }
    int getAge() {
        return age;
    }
}; 

在上面的例子中,Person是一个类名,name和age是属性,setName、setAge、getName和getAge是方法。这个类可以用来描述一个人的姓名和年龄。

2、比较C++中的结构和类的异同。

C++中的结构和类都是用户自定义的数据类型,但它们有以下异同:

  1. 定义方式不同:结构使用struct关键字定义,而类使用class关键字定义。
  2. 默认访问权限不同:结构中的成员默认为public,而类中的成员默认为private。
  3. 功能不同:结构主要用于描述简单的数据结构,而类可以描述更复杂的数据结构,包括属性和方法。
  4. 继承方式不同:结构不支持继承,而类支持单继承和多继承。
  5. 对象的初始化方式不同:结构可以使用初始化列表或默认构造函数进行初始化,而类可以使用构造函数进行初始化。
  6. 对象的拷贝方式不同:结构可以使用浅拷贝或深拷贝进行拷贝,而类需要定义拷贝构造函数和赋值运算符进行拷贝。

总的来说,结构和类都是用于描述数据类型的工具,但类更加强大和灵活,可以描述更复杂的数据结构和行为。

3、什么是对象,类和对象之间有着什么样的关系?

对象是类的实例,是类中定义的属性和方法的具体实现。
类是对象的模板,定义了对象的属性和行为。
类和对象之间的关系可以理解为蓝图和建筑物之间的关系,蓝图定义了建筑物的结构和特征,而建筑物则是蓝图的具体实现。在程序中,类定义了数据类型和函数,而对象则是这些数据类型和函数的实例。通过创建对象,我们可以使用类中定义的属性和方法来完成特定的任务。因此,类和对象之间是一种模板和实例的关系。

4、对象能够访问什么类型的成员?如何访问?

对象能够访问类中的公有成员和私有成员。
公有成员可以被对象直接访问,而私有成员只能通过类中的公有成员函数来访问。
访问公有成员可以使用点运算符(.)或箭头运算符(->),例如:

Person p; 
p.setName("Tom"); 
cout << p.getName() << endl;
访问私有成员需要通过公有成员函数来访问,例如:
class Person{
private:
string name;
public:
void setName(string n){
name = n;
}
string getName(){
return name;
}
void printName(){
cout<<name<<endl;
}
};
Person p;
p.setName("Tom");
p.printName();

在上面的例子中,printName是一个公有成员函数,用于访问私有成员name。通过调用printName函数,可以输出对象p的姓名属性。

5、*this指针具有什么样的特殊性?在编程过程中如何使用*this指针?

*this指针是一个指向当前对象的指针,具有以下特殊性:

  1. 只能在成员函数中使用:因为*this指针是指向当前对象的指针,所以只能在成员函数中使用。
  2. 可以用于返回对象本身:在成员函数中,可以使用*this指针返回对象本身,例如:
Person &Person::setName(string n){
this->name = n;
return *this;
}
  1. 可以用于访问对象的成员:在成员函数中,可以使用*this指针访问对象的成员,例如:
void Person::printName(){
cout << this->name <<endl;
}

在编程过程中,可以使用*this指针来访问对象的成员,或者返回对象本身。例如,在一个类的成员函数中,可以使用*this指针来访问对象的属性和方法,或者返回对象本身,以便进行链式调用。例如:

Person p;
p.setName(Tom).setAge(20);

在上面的例子中,setName和setAge都返回*this指针,使得可以进行链式调用,更加简洁和易于阅读。

6、在类中是如何实现封装性的?

在类中实现封装性的关键是将数据和方法封装在一起,并将其访问权限限制在类的内部。具体来说,可以使用访问控制符(publicprivateprotected)来控制成员的访问权限,将属性和方法分别定义为私有成员和公有成员。私有成员只能在类的内部访问,而公有成员可以被类的外部访问。这样可以保护数据不被外部直接访问和修改,同时也可以隐藏实现细节,使得代码更加清晰和易于维护。
另外,可以使用成员函数来访问私有成员,将数据的访问和修改封装在类的内部。成员函数可以访问类的私有成员,同时也可以对数据进行一些处理和验证,保证数据的正确性和安全性。这样可以避免外部直接访问和修改数据,提高代码的安全性和可维护性。
总的来说,封装性是面向对象编程的重要特性之一,可以保护数据不被外部直接访问和修改,同时也可以隐藏实现细节,使得代码更加清晰和易于维护。在类中实现封装性的关键是将数据和方法封装在一起,并将其访问权限限制在类的内部。

第9章 类及对象的封装性 习题

1、定义一个描述学生基本情况的类,数据成员包括姓名、学号、英语、数学、计算机成绩。成员函数包括设置姓名、学号和三门课的成绩、输出数据、以及求平均成绩。

#include <iostream>
#include <string>

using namespace std;

class Student {
private:
    string name;
    string id;
    float english;
    float math;
    float computer;

public:
    // 设置姓名和学号
    void setName(string name) {
        this->name = name;
    }
    void setId(string id) {
        this->id = id;
    }

    // 设置三门课成绩
    void setScores(float english, float math, float computer) {
        this->english = english;
        this->math = math;
        this->computer = computer;
    }

    // 求平均成绩
    float getAverage() {
        return (english + math + computer) / 3;
    }

    // 输出数据
    void print() {
        cout << "Name: " << name << endl;
        cout << "ID: " << id << endl;
        cout << "English: " << english << endl;
        cout << "Math: " << math << endl;
        cout << "Computer: " << computer << endl;
        cout << "Average score: " << getAverage() << endl;
    }
};

int main() {
    Student s;
    s.setName("Tom");
    s.setId("1001");
    s.setScores(89.5, 93.2, 87.8);
    s.print();

    return 0;
}

在这个示例程序中,我们定义了一个Student类,其中包括了姓名、学号和三门课的成绩等数据成员,以及设置姓名、学号和三门课成绩、求平均成绩、输出数据等成员函数。在main()函数中,我们创建了一个Student对象s,并设置了相关的数据成员,最后输出了这个对象的数据。

2、定义一个满足如下要求的类CDate:

  1. 有三个成员数据:年、月、日;
  2. 有设置日期的成员函数;
  3. 有用格式“月/日/年“输出日期的成员函数;
  4. 有对当前日期加一天的成员函数;
#include<iostream>
using namespace std;

class CDate {
private:
    int year;
    int month;
    int day;

public:
    // 构造函数,初始化日期
    CDate(int y=0, int m=0, int d=0) {
        year = y;
        month = m;
        day = d;
    }

    // 成员函数,设置日期
    void setDate(int y, int m, int d) {
        year = y;
        month = m;
        day = d;
    }

    // 成员函数,用格式“月/日/年”输出日期
    void printDate() {
        cout << month << "/" << day << "/" << year << endl;
    }

    // 成员函数,对当前日期加一天
    void nextDay() {
        int daysInMonth;

        // 判断当前月份天数
        switch (month) {
            case 2:
                // 如果当前是闰年,二月有29天
                if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
                    daysInMonth = 29;
                // 否则二月有28天
                else
                    daysInMonth = 28;
                break;

            case 4:
            case 6:
            case 9:
            case 11:
                daysInMonth = 30;
                break;

            default:
                daysInMonth = 31;
        }

        // 如果当前天数等于当月天数,进位
        if (day == daysInMonth) {
            day = 1;

            // 如果当前月份等于12,进位到下一年
            if (month == 12) {
                month = 1;
                year++;
            } else {
                month++;
            }
        } else {
            day++;
        }
    }
};

int main() {
    CDate date(2021, 3, 31);    // 初始化日期为2021年3月31日
    date.printDate();           // 输出日期
    date.nextDay();             // 将日期加一天
    date.printDate();           // 输出加一天后的日期

    return 0;
}

第10章 类的成员 思考题

1、构造函数与析构函数分别有什么作用?为什么允许构造函数重载而不允许析构函数重载?

构造函数用于初始化对象的数据成员,可以在对象创建时自动调用。
析构函数用于清理对象占用的资源,可以在对象销毁时自动调用。构造函数允许重载是因为不同的对象可能需要不同的初始化方式,而析构函数不允许重载是因为对象销毁时只需要执行一次清理操作。

2、如何实现对象成员的初始化?试举例说明。

对象成员的初始化可以在构造函数中使用初始化列表来实现。例如:

class Person {
public:
    Person(int age, const std::string& name) : m_age(age), m_name(name) {}
private:
    int m_age;
    std::string m_name;
};

3、const对象具有什么特征?在使用const对象时的注意点是什么?

const对象不能被修改,具有只读特性。在使用const对象时需要注意不能调用非const成员函数,也不能修改对象的数据成员。

4、如何声明一个友元函数?一个类的友元函数是如何访问该类的成员的?

声明友元函数需要在类的声明中使用friend关键字。一个类的友元函数可以访问该类的私有成员,但不是该类的成员函数。

class Person {
public:
    friend void printAge(const Person& p);
private:
    int m_age;
};

void printAge(const Person& p) {
    std::cout << p.m_age << std::endl;
}

5、静态成员数据具有什么特征?在什么情况下使用静态成员数据?

静态成员数据是属于类的,而不是属于对象的,所有对象共享同一个静态成员数据。静态成员数据可以用于记录类的某些属性或状态,例如记录类的实例个数。

6、静态成员函数具有什么特征?在定义静态成员函数时要注意什么?

静态成员函数不能访问非静态成员数据,也不能使用this指针。在定义静态成员函数时需要在函数名前加上static关键字。例如:

class Person {
public:
    static int getCount() { return s_count; }
private:
    static int s_count;
};

int Person::s_count = 0;

第10章 类的成员 习题

1、使用函数重载的方法定义两个重名函数,分别求出整型数的两点间距离和实型数的两点间距离。

#include <iostream>
#include <cmath>
using namespace std;
// 计算整型数的两点间距离
double distance(int x1, int y1, int x2, int y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
// 计算实型数的两点间距离
double distance(double x1, double y1, double x2, double y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int main() {
    int x1 = 1, y1 = 2, x2 = 3, y2 = 4;
    double dx1 = 1.0, dy1 = 2.0, dx2 = 3.0, dy2 = 4.0;

    // 调用整型数版本的distance函数
    cout << "The distance between two points (int version): " << distance(x1, y1, x2, y2) << endl;

    // 调用实型数版本的distance函数
    cout << "The distance between two points (double version): " << distance(dx1, dy1, dx2, dy2) << endl;

    return 0;
}

2、定义一个Cpoint类,用成员函数的重载实现上题的功能。并编写主函数测试。

#include <iostream>
#include <cmath>

using namespace std;

class Cpoint {
private:
    double x, y;
public:
    Cpoint(double _x = 0.0, double _y = 0.0) : x(_x), y(_y) {}
    double distance(const Cpoint &p) const {
        return sqrt(pow(x - p.x, 2) + pow(y - p.y, 2));
    }
    int distance(const Cpoint &p) const {
        return static_cast<int>(round(sqrt(pow(x - p.x, 2) + pow(y - p.y, 2))));
    }
};

int main() {
    Cpoint p1(0.0, 0.0), p2(3.0, 4.0);
    cout << "distance between p1 and p2 is " << p1.distance(p2) << endl;
    cout << "distance between p1 and p2 is " << p1.distance(p2) << endl;
    return 0;
}

其中,distance函数被重载为返回double类型和int类型的距离,分别表示实型和整型两种情况。可以通过输入两个点的坐标构造Cpoint类的对象,然后分别调用重载的distance函数计算距离并输出。

3、使用重载的方法编写一个求整数、实数和双精度数的平方数的类。并用一个主函数进行测试。

#include <iostream>
#include <cmath>
using namespace std;

class Square {
public:
    int square(int x) {
        return x * x;
    }
    
    double square(double x) {
        return x * x;
    }
    
    double square(long double x) {
        return x * x;
    }
};

int main() {
    Square s;
    int a = 5;
    double b = 2.5;
    long double c = 1.234567890123456789;

    cout << "The square of " << a << " is " << s.square(a) << endl;
    cout << "The square of " << b << " is " << s.square(b) << endl;
    cout << "The square of " << c << " is " << s.square(c) << endl;

    return 0;
}

该程序定义了一个Square类,其中有三个重载的square函数,分别用于计算整数、实数和双精度数的平方。在主函数中,创建Square类的一个实例s,并分别用不同的数据类型调用square函数,输出计算结果。

4、定义一个描述复数的类,成员数据包括实部和虚部。成员函数包括构造函数和输出复数。并编写主函数来测试数据与成员函数的正确性。

#include <iostream>
using namespace std;

class Complex {
private:
    double real, imag; //实部和虚部
public:
    //构造函数
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    //输出函数
    void print() const {
        cout << "(" << real << ", " << imag << ")" << endl;
    }
};
int main() {
    Complex c(3.2, 1.5); //创建一个复数对象
    c.print(); //输出复数值

    return 0;
}

5、设计一个类,实现求n的阶乘。并编写程序输出8的阶乘。

#include <iostream>
using namespace std;

class Factorial {
public:
    int compute(int n) {
        if (n == 0) {
            return 1;
        } else {
            return n * compute(n - 1);
        }
    }
};

int main() {
    Factorial f;
    int n = 8;
    int result = f.compute(n);
    cout << n << "的阶乘是:" << result << endl;
    return 0;
}

6、设计一个类,实现十进制到十六进制的转换。并编写主函数进行测试。

#include <string>
using namespace std;

class DecimalToHex {
private:
    int decimal;
    string hex;

public:
    DecimalToHex(int d) : decimal(d) {}

    void convert() {
        string hex_digits = "0123456789ABCDEF";
        int quotient = decimal;

        while (quotient > 0) {
            int remainder = quotient % 16;
            hex = hex_digits[remainder] + hex;
            quotient /= 16;
        }
    }

    void print() {
        cout << hex << endl;
    }
};
#include <iostream>
using namespace std;

int main() {
    DecimalToHex dth(4567);
    dth.convert();
    dth.print();

    return 0;
}

7、设计一个类,类中包括成员数据以及求两个数的最大公约数和最小公倍数的成员函数。并编写主函数进行测试。

#include <iostream>

class GCD_LCM {
public:
    GCD_LCM(int a, int b) : num1(a), num2(b) {}

    int gcd() {  // 求最大公约数
        int a = num1, b = num2;
        while (b != 0) {
            int temp = a % b;
            a = b;
            b = temp;
        }
        return a;
    }

    int lcm() {  // 求最小公倍数
        return num1 * num2 / gcd();
    }

private:
    int num1, num2;
};

int main() {
    int a, b;
    std::cout << "Please enter two integers: ";
    std::cin >> a >> b;

    GCD_LCM obj(a, b);

    std::cout << "The GCD of " << a << " and " << b << " is " << obj.gcd() << std::endl;
    std::cout << "The LCM of " << a << " and " << b << " is " << obj.lcm() << std::endl;

    return 0;
}

在上述代码中,GCD_LCM 类有两个私有成员变量 num1 和 num2,分别表示要求最大公约数和最小公倍数的两个整数。类中有两个公有成员函数 gcd() 和 lcm(),分别用于求最大公约数和最小公倍数。在 main() 函数中,先读入两个整数,然后创建一个 GCD_LCM 对象并调用其 gcd() 和 lcm() 函数来计算最大公约数和最小公倍数。最后将结果输出。

第10章 类的成员 实验题

2、编写一个实现两个数相减的类的测试程序,请写出类的定义,构成一个完整的程序,要求调用类的构造函数和析构函数时均有明确的输出信息。程序主函数如下:

void main(){
	Test t(88,32);
	t.print();
}

答:

#include <iostream>
using namespace std;
class Test {
private:
    int a, b;
public:
    Test(int x, int y) {  // 构造函数
        a = x;
        b = y;
        cout << "调用了构造函数" << endl;
    }
    ~Test() {  // 析构函数
        cout << "调用了析构函数" << endl;
    }
    void print() {  // 输出相减结果
        cout << a << " - " << b << " = " << a-b << endl;
    }
};
int main() {
    Test t(88, 32);
    t.print();
    return 0;
}

运行结果:

调用了构造函数
88 - 32 = 56
调用了析构函数

3、编写一个类Cpoint表示一个点的信息。在此基础上编写一个表示三角形的类tria,顶点为其对象成员。编写完整的程序输出三角形的面积。

#include <iostream>
#include <cmath>

using namespace std;

class Cpoint {
public:
    Cpoint(double x = 0, double y = 0) : m_x(x), m_y(y) {
        cout << "Cpoint constructor called." << endl;
    }
    ~Cpoint() {
        cout << "Cpoint destructor called." << endl;
    }
    double getX() const {
        return m_x;
    }
    double getY() const {
        return m_y;
    }
private:
    double m_x;
    double m_y;
};

class tria {
public:
    tria(const Cpoint& p1, const Cpoint& p2, const Cpoint& p3) : m_p1(p1), m_p2(p2), m_p3(p3) {
        cout << "tria constructor called." << endl;
    }
    ~tria() {
        cout << "tria destructor called." << endl;
    }
    double area() const {
        double a = sqrt(pow(m_p2.getX() - m_p1.getX(), 2) + pow(m_p2.getY() - m_p1.getY(), 2));
        double b = sqrt(pow(m_p3.getX() - m_p2.getX(), 2) + pow(m_p3.getY() - m_p2.getY(), 2));
        double c = sqrt(pow(m_p1.getX() - m_p3.getX(), 2) + pow(m_p1.getY() - m_p3.getY(), 2));
        double s = (a + b + c) / 2.0;
        return sqrt(s * (s - a) * (s - b) * (s - c));
    }
private:
    Cpoint m_p1;
    Cpoint m_p2;
    Cpoint m_p3;
};

int main() {
    Cpoint p1(0, 0);
    Cpoint p2(3, 0);
    Cpoint p3(0, 4);
    tria t(p1, p2, p3);
    cout << "The area of the triangle is: " << t.area() << endl;
    return 0;
}

输出结果:

Cpoint constructor called.
Cpoint constructor called.
Cpoint constructor called.
tria constructor called.
The area of the triangle is: 6
tria destructor called.
Cpoint destructor called.
Cpoint destructor called.
Cpoint destructor called.

4、将上题用于输出三角形面积的成员函数改为用友元函数来实现。

#include <iostream>
#include <cmath>
using namespace std;

class CPoint; // 前置声明

class CTria {
private:
    CPoint p1, p2, p3;
public:
    CTria(CPoint a, CPoint b, CPoint c): p1(a), p2(b), p3(c) {}
    friend double getArea(CTria t); // 声明友元函数
};

class CPoint {
private:
    double x, y;
public:
    CPoint(double a = 0, double b = 0): x(a), y(b) {}
    friend double getDistance(CPoint a, CPoint b);
    friend double getArea(CTria t);
};

double getDistance(CPoint a, CPoint b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double getArea(CTria t) {
    double a = getDistance(t.p1, t.p2);
    double b = getDistance(t.p2, t.p3);
    double c = getDistance(t.p3, t.p1);
    double p = (a + b + c) / 2;
    return sqrt(p * (p - a) * (p - b) * (p - c));
}

int main() {
    CPoint p1(0, 0), p2(3, 0), p3(0, 4);
    CTria t(p1, p2, p3);
    cout << "三角形的面积为:" << getArea(t) << endl;
    return 0;
}

5、设计一个名为football的类,用于统计一个俱乐部的联赛成绩。其中使用一个静态成员数据totals来存储总分以及一个静态成员函数re_totals()返回该总分。并编写相应的测试程序。

#include <iostream>
using namespace std;

class Football {
public:
    Football(int s) : score(s) {
        totals += score;
        count++;
        cout << "Add a score: " << score << endl;
    }

    static int re_totals() {
        return totals;
    }

    static int re_count() {
        return count;
    }

private:
    int score;
    static int totals;
    static int count;
};

int Football::totals = 0;
int Football::count = 0;

int main() {
    Football f1(2);
    Football f2(3);
    Football f3(1);

    cout << "The total scores: " << Football::re_totals() << endl;
    cout << "The number of scores: " << Football::re_count() << endl;

    return 0;
}

输出结果:

Add a score: 2
Add a score: 3
Add a score: 1
The total scores: 6
The number of scores: 3

在上面的示例中,Football类中定义了一个静态成员变量totals用于统计总分,以及一个静态成员变量count用于统计比赛场次。每次添加一个得分时,totals和count都会更新。在程序中使用Football类的静态成员函数re_totals()和re_count()获取统计结果。