Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

C++ 学习项目 - TD0 和 TD1

TD0 - 复习 (Sujet & Solution)

练习1:基本计算器

1-a:简单计算器

题目要求: 编写一个(C++)用户友好的程序,要求用户输入2个整数。程序然后显示:这2个数字的和、差、积、商、整数除法的余数。

解答:

int main() {
    int num1, num2;
    
    std::cout << "欢迎使用整数计算器!" << std::endl;
    std::cout << "请输入第一个整数: ";
    std::cin >> num1;
    std::cout << "请输入第二个整数: ";
    std::cin >> num2;
    
    std::cout << "\n计算结果:" << std::endl;
    std::cout << "和: " << num1 << " + " << num2 << " = " << num1 + num2 << std::endl;
    std::cout << "差: " << num1 << " - " << num2 << " = " << num1 - num2 << std::endl;
    std::cout << "积: " << num1 << " × " << num2 << " = " << num1 * num2 << std::endl;
    
    if (num2 != 0) {
        std::cout << "商: " << num1 << " ÷ " << num2 << " = " << static_cast<double>(num1) / num2 << std::endl;
        std::cout << "整除的余数: " << num1 << " % " << num2 << " = " << num1 % num2 << std::endl;
    } else {
        std::cout << "商: 无法计算(除数不能为零)" << std::endl;
        std::cout << "整除的余数: 无法计算(除数不能为零)" << std::endl;
    }
    
    return 0;
}

关键点:

  • 使用 static_cast<double> 进行类型转换以获得浮点除法结果
  • 检查除数为零的情况以避免程序崩溃

1-b:带运算符选择的计算器

题目要求: 修改这个程序,询问用户想要执行哪种运算。计算将在函数中进行(传递2个数字和要执行的操作)。

程序执行示例:

输入第一个数字:12
输入第二个数字:2
要执行的操作?(+, -, *, /, %):*
12*2 = 24

解答:

double calculate(int num1, int num2, char op) {
    switch(op) {
        case '+': return num1 + num2;
        case '-': return num1 - num2;
        case '*': return num1 * num2;
        case '/':
            if (num2 != 0) return static_cast<double>(num1) / num2;
            else { std::cout << "错误:除数不能为零!" << std::endl; return 0; }
        case '%':
            if (num2 != 0) return num1 % num2;
            else { std::cout << "错误:除数不能为零!" << std::endl; return 0; }
        default:
            std::cout << "错误:无效的运算符!" << std::endl;
            return 0;
    }
}

int main() {
    int num1, num2;
    char op;
    
    std::cout << "输入第一个数字:";
    std::cin >> num1;
    std::cout << "输入第二个数字:";
    std::cin >> num2;
    std::cout << "要执行的操作?( +, -, *, /, % ):";
    std::cin >> op;
    
    double result = calculate(num1, num2, op);
    
    if (op == '+' || op == '-' || op == '*' || op == '/' || op == '%') {
        std::cout << num1 << op << num2 << " = " << result << std::endl;
    }
    
    return 0;
}

练习2:数组和函数

2-a:基本数组输入输出

题目要求: 编写一个程序,允许输入实数数组的内容。程序然后显示输入的值。要输入的值的数量(元素数量)由用户提供(暂时使用静态内存分配:double tab[100];)。

解答:

int main() {
    double tab[100];
    int size;
    
    std::cout << "请输入数组大小(最大100):";
    std::cin >> size;
    
    if (size <= 0 || size > 100) {
        std::cout << "错误:数组大小必须在1到100之间" << std::endl;
        return 1;
    }
    
    std::cout << "请输入 " << size << " 个实数:" << std::endl;
    for (int i = 0; i < size; i++) {
        std::cout << "元素 " << i + 1 << ": ";
        std::cin >> tab[i];
    }
    
    std::cout << "\n数组内容:" << std::endl;
    for (int i = 0; i < size; i++) {
        std::cout << "tab[" << i << "] = " << tab[i] << std::endl;
    }
    
    return 0;
}

2-b:添加显示函数AfficherTableau

题目要求: 创建过程AfficherTableau,允许显示数组的所有元素(数组的元素数量必须作为参数传递)。修改程序以使用此函数。

解答:

void AfficherTableau(const double tableau[], int taille) {
    std::cout << "\n数组内容:" << std::endl;
    for (int i = 0; i < taille; i++) {
        std::cout << "tableau[" << i << "] = " << tableau[i] << std::endl;
    }
}

2-c:添加输入函数SaisirNbElements

题目要求: 创建函数SaisirNbElements,询问用户要输入的元素数量。在前面的程序中使用此函数。

解答:

int SaisirNbElements() {
    int taille;
    std::cout << "请输入数组大小(最大100):";
    std::cin >> taille;
    
    while (taille <= 0 || taille > 100) {
        std::cout << "错误:数组大小必须在1到100之间" << std::endl;
        std::cout << "请重新输入数组大小:";
        std::cin >> taille;
    }
    
    return taille;
}

2-d:添加过程SaisirTableau

题目要求: 在前面的程序中添加过程SaisirTableau

解答:

void SaisirTableau(double tableau[], int taille) {
    std::cout << "请输入 " << taille << " 个实数:" << std::endl;
    for (int i = 0; i < taille; i++) {
        std::cout << "元素 " << i + 1 << ": ";
        std::cin >> tableau[i];
    }
}

2-e:动态内存分配

题目要求: 修改程序以对数组使用动态分配:要输入的元素数量将等于数组的大小。

动态分配示例:

int nb;
double *tab = NULL; // 指针声明 + 赋值
cout << "元素数量?";
cin >> nb;
tab = new double[nb]; // 分配内存空间
... // 使用数组
...
delete[] tab; // 释放内存空间

解答:

int main() {
    int nb;
    double *tab = NULL;
    
    nb = SaisirNbElements();
    tab = new double[nb];
    
    if (tab == NULL) {
        std::cout << "错误:内存分配失败!" << std::endl;
        return 1;
    }
    
    SaisirTableau(tab, nb);
    AfficherTableau(tab, nb);
    
    delete[] tab;
    tab = NULL;
    
    return 0;
}

2-f:数组求和函数AdditionneTableaux

题目要求: 编写函数AdditionneTableaux,允许对两个相同大小的数组进行逐项求和。用2个数组(tab1 + tab2)测试此函数,然后用3个数组(目标是对三个数组进行逐项求和)。

解答:

// 两个数组的求和
void AdditionneTableaux(const double tab1[], const double tab2[], double result[], int taille) {
    for (int i = 0; i < taille; i++) {
        result[i] = tab1[i] + tab2[i];
    }
}

// 三个数组的求和(函数重载)
void AdditionneTableaux(const double tab1[], const double tab2[], const double tab3[], double result[], int taille) {
    for (int i = 0; i < taille; i++) {
        result[i] = tab1[i] + tab2[i] + tab3[i];
    }
}

TD1 - Tableau类 (Sujet & Solution)

练习1:TableauDouble类建模

1-a:UML建模TableauDouble类

题目要求: 用UML建模TableauDouble类。字段将是:

  • Pt:指向double的指针
  • Taille:存储的double数量
  • Nom:数组的名称(用于显示值)

类的基本功能将是:

  • 对数组大小的读写访问
  • 对数组每个元素的读写访问
  • 对数组名称的读写访问
  • 与另一个TableauDouble对象的标量积
  • 显示数组内容和名称
  • 用户构造函数,传递数组大小
  • 拷贝构造函数
  • 析构函数

UML设计:

+-----------------------------------+
|          TableauDouble            |
+-----------------------------------+
| - Pt : double*                    |
| - Taille : unsigned int           |
| - Nom : string                    |
+-----------------------------------+
| + TableauDouble(taille, nom, val) |
| + TableauDouble(const TableauDouble&)|
| + ~TableauDouble()                |
| + GetTaille() : unsigned int      |
| + SetTaille(taille : unsigned int)|
| + GetNom() : string               |
| + SetNom(nom : const string&)     |
| + GetElement(i : unsigned int) : double |
| + SetElement(i : unsigned int, v : double) |
| + Plus(td : const TableauDouble&) : TableauDouble |
| + ProduitScalaire(td : const TableauDouble&) : double |
| + Afficher() : void               |
| + Copie(tableau : const TableauDouble&) |
| + operator=(t : const TableauDouble&) : TableauDouble& |
+-----------------------------------+

1-b:关系和多重性

题目: 什么关系和多重性建模TableauDouble类与其字段类之间的链接?请说明理由。

解答: TableauDouble类与其字段类之间的关系是组合关系(composition),多重性为1:1,因为:

  • TableauDouble对象完全拥有其double数组的生命周期
  • 数组不能独立于TableauDouble对象存在
  • 当TableauDouble对象被销毁时,数组也会被销毁

1-c:字段可见性

题目: TableauDouble的3个字段应使用什么可见性?

解答: 3个字段都应该使用private可见性:

  • Pt:指向动态分配内存的指针,外部不应直接访问
  • Taille:数组大小,应通过getter/setter方法控制访问
  • Nom:数组名称,应通过getter/setter方法控制访问

1-d:Taille字段的写访问方法

题目: 在C++中,Taille字段的写访问方法需要实现哪些步骤?

解答: SetTaille方法需要实现以下步骤:

  1. 释放当前动态分配的内存 (delete[] Pt)
  2. 更新Taille字段为新值
  3. 分配新的内存空间 (Pt = new double[Taille])
  4. 初始化新数组的元素(通常为0)

1-e:为什么不使用值传递

题目: 在C++中,为什么不应该使用参数值传递来传递TableauDouble类型的对象?

解答: 不应该使用参数值传递是因为:

  • TableauDouble包含动态分配的内存
  • 值传递会触发拷贝构造函数,创建整个数组的副本
  • 这会导致不必要的内存分配和数据复制,影响性能
  • 应该使用引用传递(const TableauDouble&

1-f:为什么需要析构函数

题目: 在C++中,为什么需要在这个类中编写析构函数?在UML类图中显示它有什么意义?

解答: 需要编写析构函数是因为:

  • TableauDouble类使用动态内存分配(new
  • 如果不手动释放内存(delete[]),会导致内存泄漏
  • 析构函数确保对象销毁时自动释放所占用的内存
  • 在UML类图中显示析构函数表明该类管理资源,需要特殊的清理操作

练习2:实现和测试

2-a:Copie函数注释

题目: 为函数TableauDouble::Copie(...)添加注释。

解答:

//1- Que va faire, en gros, cette méthode?
//   Cette méthode copie le contenu d'un tableau dans l'objet courant
//1- Pourquoi le const? Est il obligatoire?
//   Le const garantit que le tableau source ne sera pas modifié. C'est une bonne pratique.
//1- Pourquoi la référence?
//   La référence évite la copie inutile de l'objet lors du passage de paramètre
void TableauDouble::Copie(const TableauDouble &tableau)
{
    if( this != &tableau ) // 5- A quoi sert cette ligne? 
                          //    Cette ligne évite l'auto-assignation (a.Copie(a))
        {
        Nom = tableau.Nom;       //2- Copie du nom
        Taille = tableau.Taille; //2- Copie de la taille
        delete[] Pt;             //3- Libération de l'ancienne mémoire
        Pt = new double[Taille]; //3- Allocation de nouvelle mémoire
        for(unsigned int i=0; i<Taille; i++) //4- Boucle de copie
            Pt[i] = tableau.Pt[i];           //4- Copie élément par élément
        }
}

2-b:完成GetTaille和SetNom

题目: 在TableauDouble.cpp中完成函数GetTailleSetNom

解答:

unsigned int TableauDouble::GetTaille() const
{
    return Taille;
}

void TableauDouble::SetNom(const string &nom)
{
    Nom = nom;
}

2-c:SetTaille函数

题目: 参考函数TableauDouble::Copie编写函数TableauDouble::SetTaille

解答:

void TableauDouble::SetTaille( unsigned int taille )
{
    // Libérer l'ancien tableau
    delete[] Pt;
    
    // Mettre à jour la taille
    Taille = taille;
    
    // Allouer un nouveau tableau avec la nouvelle taille
    Pt = new double[Taille];
    
    // Initialiser les éléments à 0
    for(unsigned int i=0; i<Taille; i++)
        Pt[i] = 0.0;
}

2-d:拷贝构造函数

题目: 仍然参考函数TableauDouble::Copie,创建拷贝构造函数。为什么它对TableauDouble类是必需的?

解答:

TableauDouble::TableauDouble( const TableauDouble &t)
{
    Nom = t.Nom;
    Taille = t.Taille;
    Pt = new double[Taille];
    for(unsigned int i=0; i<Taille; i++)
        Pt[i] = t.Pt[i];
}

为什么必需: 拷贝构造函数对TableauDouble类是必需的,因为:

  • 默认拷贝构造函数只进行浅拷贝
  • 这会导致多个对象共享同一块内存
  • 当对象销毁时会出现双重释放的错误
  • 自定义拷贝构造函数确保深拷贝,每个对象有独立的内存

2-e:Plus函数

题目: 添加并实现成员函数Plus,实现两个相同大小数组的逐项求和。

使用示例:

TableauDouble a(10),b(10);
// 初始化数组的值
...
TableauDouble v( a.Plus(b) );
v.Affiche();

解答:

TableauDouble TableauDouble::Plus(const TableauDouble &td) const
{
    // 确定两个数组中较小的大小
    unsigned int min_taille = (this->Taille < td.Taille) ? this->Taille : td.Taille;

    // 创建新的TableauDouble对象存储结果
    TableauDouble result(min_taille, this->Nom + "+" + td.Nom);

    // 执行逐项相加
    for (unsigned int i = 0; i < min_taille; i++)
    {
        result.SetElement(i, this->GetElement(i) + td.GetElement(i));
    }

    return result;
}

2-e':ProduitScalaire函数

题目: 按照相同的使用原则,添加并实现成员函数ProduitScalaire来计算两个数组的标量积。

解答:

double TableauDouble::ProduitScalaire(const TableauDouble &td) const
{
    // 如果大小不同,使用较小的大小
    unsigned int min_taille = (this->Taille < td.Taille) ? this->Taille : td.Taille;
    
    double resultat = 0.0;
    for (unsigned int i = 0; i < min_taille; i++)
    {
        resultat += this->GetElement(i) * td.GetElement(i);
    }
    
    return resultat;
}

2-f:Plus函数使用问题

题目: Plus函数的使用不太方便...我们更希望使用以下行:

TableauDouble w(1);
w = a.Plus(b);
w.Affiche();

逐步执行(调试)前面的行,确定这3行代码执行了哪些方法(时间顺序)?为什么这个代码不工作?

解答分析: 代码执行顺序:

  1. TableauDouble w(1); - 调用构造函数创建w
  2. w = a.Plus(b); - 调用Plus方法创建临时对象,然后调用默认赋值操作符
  3. w.Affiche(); - 调用Affiche方法
  4. 临时对象析构
  5. w对象析构

问题: 这个代码不工作是因为:

  • Plus方法返回一个临时对象
  • 默认的赋值操作符只进行浅拷贝
  • 临时对象销毁时释放了内存
  • w对象指向已释放的内存,导致错误

2-g:operator=实现

题目: 为了解决前面的问题,我们建议重载C++的操作符=。

声明:

TableauDouble & operator=(const TableauDouble &t);

推荐模型:

TableauDouble & TableauDouble::operator=(const TableauDouble &t)
{ 
    if( this != &t )
    { 
        // 释放当前对象的指令
        // 然后重新分配为't'的大小
        // 然后逐值复制t的元素
    }
    return (*this);
}

解答:

TableauDouble & TableauDouble::operator=(const TableauDouble &t) 
{
    if( this != &t ) 
    { 
        // Instructions pour désallouer l'objet en cours
        delete[] Pt;
        
        // Puis réallouer à la taille de 't'
        Nom = t.Nom;
        Taille = t.Taille;
        Pt = new double[Taille];
        
        // Puis copier, valeurs par valeurs les éléments de t
        for(unsigned int i=0; i<Taille; i++)
            Pt[i] = t.Pt[i];
    }
    return (*this);
}

编译和运行

# 创建并进入build目录
mkdir -p build && cd build

# 生成Makefile并编译
cmake .. && make

# 运行程序
./MyCppProject

运行示例:

#### TD TableauDouble #### 
Entrer le nom du tableau : MonTableau
Entrer la taille du tableau : 3
MonTableau[0] = 1.5
MonTableau[1] = 2.5  
MonTableau[2] = 3.5
MonTableau [3] = [ 1.5, 2.5, 3.5 ]

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages