题目要求: 编写一个(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>进行类型转换以获得浮点除法结果 - 检查除数为零的情况以避免程序崩溃
题目要求: 修改这个程序,询问用户想要执行哪种运算。计算将在函数中进行(传递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;
}题目要求: 编写一个程序,允许输入实数数组的内容。程序然后显示输入的值。要输入的值的数量(元素数量)由用户提供(暂时使用静态内存分配: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;
}题目要求: 创建过程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;
}
}题目要求: 创建函数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;
}题目要求: 在前面的程序中添加过程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];
}
}题目要求: 修改程序以对数组使用动态分配:要输入的元素数量将等于数组的大小。
动态分配示例:
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;
}题目要求: 编写函数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];
}
}题目要求: 用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& |
+-----------------------------------+
题目: 什么关系和多重性建模TableauDouble类与其字段类之间的链接?请说明理由。
解答: TableauDouble类与其字段类之间的关系是组合关系(composition),多重性为1:1,因为:
- TableauDouble对象完全拥有其double数组的生命周期
- 数组不能独立于TableauDouble对象存在
- 当TableauDouble对象被销毁时,数组也会被销毁
题目: TableauDouble的3个字段应使用什么可见性?
解答: 3个字段都应该使用private可见性:
Pt:指向动态分配内存的指针,外部不应直接访问Taille:数组大小,应通过getter/setter方法控制访问Nom:数组名称,应通过getter/setter方法控制访问
题目: 在C++中,Taille字段的写访问方法需要实现哪些步骤?
解答: SetTaille方法需要实现以下步骤:
- 释放当前动态分配的内存 (
delete[] Pt) - 更新Taille字段为新值
- 分配新的内存空间 (
Pt = new double[Taille]) - 初始化新数组的元素(通常为0)
题目: 在C++中,为什么不应该使用参数值传递来传递TableauDouble类型的对象?
解答: 不应该使用参数值传递是因为:
- TableauDouble包含动态分配的内存
- 值传递会触发拷贝构造函数,创建整个数组的副本
- 这会导致不必要的内存分配和数据复制,影响性能
- 应该使用引用传递(
const TableauDouble&)
题目: 在C++中,为什么需要在这个类中编写析构函数?在UML类图中显示它有什么意义?
解答: 需要编写析构函数是因为:
- TableauDouble类使用动态内存分配(
new) - 如果不手动释放内存(
delete[]),会导致内存泄漏 - 析构函数确保对象销毁时自动释放所占用的内存
- 在UML类图中显示析构函数表明该类管理资源,需要特殊的清理操作
题目: 为函数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
}
}题目: 在TableauDouble.cpp中完成函数GetTaille和SetNom。
解答:
unsigned int TableauDouble::GetTaille() const
{
return Taille;
}
void TableauDouble::SetNom(const string &nom)
{
Nom = nom;
}题目: 参考函数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;
}题目: 仍然参考函数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类是必需的,因为:
- 默认拷贝构造函数只进行浅拷贝
- 这会导致多个对象共享同一块内存
- 当对象销毁时会出现双重释放的错误
- 自定义拷贝构造函数确保深拷贝,每个对象有独立的内存
题目: 添加并实现成员函数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;
}题目: 按照相同的使用原则,添加并实现成员函数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;
}题目: Plus函数的使用不太方便...我们更希望使用以下行:
TableauDouble w(1);
w = a.Plus(b);
w.Affiche();逐步执行(调试)前面的行,确定这3行代码执行了哪些方法(时间顺序)?为什么这个代码不工作?
解答分析: 代码执行顺序:
TableauDouble w(1);- 调用构造函数创建ww = a.Plus(b);- 调用Plus方法创建临时对象,然后调用默认赋值操作符w.Affiche();- 调用Affiche方法- 临时对象析构
- w对象析构
问题: 这个代码不工作是因为:
- Plus方法返回一个临时对象
- 默认的赋值操作符只进行浅拷贝
- 临时对象销毁时释放了内存
- w对象指向已释放的内存,导致错误
题目: 为了解决前面的问题,我们建议重载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 ]