c++实验代码及学习笔记(六)
你好! 这是一个高程实验课的代码记录及学习笔记。我将记录一些重要的知识点、易错点。但是作为大学生,水平很低,敬请指点教导、优化代码。
本实验紧接上回。本周我们学习了【数据保护与共享】,关于静态成员数据、常成员数据、常成员函数、常对象等等知识。所以为应用所学知识,我们要解决第一个问题:改进我们的有理数类。同时,学习运算符重载并改进。
第一题非常简单,选做题么,单看题目非常简单,只需支持加减法,不用考虑运算优先级。输入字符串,那我们就把字符串一个一个读下来,把分数部分创建有理数类,然后进行计算。看起来非常简单啊!
然而……
能让你怀疑人生啊!!
静态变量的好处是可以取代全局变量,它实际上是类域中的全局变量。所以静态数据成员的定义(初始化)不应该被放在头文件中,因为这样做会引起重复定义这样的错误。
参考文章:C++ 静态数据成员和静态成员函数
原理详见:(数据存储)C++类中的静态成员变量与静态成员函数
大概是这么个意思:程序要向内存爸爸申请内存,把不同的数据宝宝送到不同的内存幼儿园。内存分成四个幼儿园:堆区、栈区、代码区、全局数据区幼儿园,new产生的好动的动态数据宝宝去堆区、函数内产生的自动变量宝宝去栈区,函数over后自动变量就退出栈区了。而文静的静态数据宝宝则在最安全的全局变量区。
对于面向对象的static关键字,我们可以创建类的静态数据成员,在类中起作用。
调用静态函数:直接用类名,MyClass::function();
举例
//example 1
#include
class MyClass{
public:
MyClass(int a,int b,int c);
void GetSum();
private:
int a,b,c;
static int sum;//声明静态数据成员
};
int MyClass::sum=0;//定义并初始化静态数据成员
需要注意的是,虽然在类内声明,但静态数据成员的初始化要在类外,用形如 int ClassName::sum=0;这样的形式。(VS6以上不支持在类内定义)
常成员变量/函数在形式上的特征是加了 const 这个关键字
主要是程序员希望这个变量/函数不要轻易被改变,使用起来更方便等等。
参考文章:C++ const用法 尽可能使用const (const修饰指针变量、函数参数、成员函数、函数返回值)
C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不变的。如果在编程中确实有某个值保持不变,就应该明确使用const,这样可以获得编译器的帮助。
const修饰成员函数不能修改任何成员变量,且不能调用任何非const成员函数。
const修饰函数参数,则该参数传入后不可被修改。
这其实就是上述静态、常成员变量/函数的一种应用
参考文章:C++运算符重载基础教程
C++实现有理数类加减乘除
关于运算符重载,教程说明的非常详细,仔细阅读,其实上手写一遍就会。
其实质是函数重载,如用类的数据类型定义,则该函数运算符重载只对该类有效。
给出重载+的示例,可仿照写出- * \ 的函数
public:
Rational operator+(const Rational &r2) const
{
Rational r;
r.num = this->num*r2.den + r2.num*this->den;
r.den = this->den*r2.den;
r.simple();
return r;
}
这道题乍一看并不难,输入字符串,转换为字符型/整型,然后依次读入,因为不要求括号、乘除,不涉及优先级运算,所以本题无需使用堆栈或递归。使用if判断识别,将分数储存为有理数类,再进行加减运算,最后使用有理数类的函数输出。
但是,在设计函数、具体实现的时候,只会面向百度编程的二狗遭遇了前所未有的困难。
所以这道题建议大家好好思考,再来阅读本篇回答。
这道题请了场外援助皮皮,皮皮采用暴力拆解的思想,是实现本题的简单方法。而面向百度编程的二狗坚持认为其中会用到递归或者堆栈,双方产生了极大的矛盾。但是编程能力极弱的菜狗并没能实现她的思想,在暴力拆解的方法上也遇到问题。
比如
最终皮皮来为我们解答:
int main(){
...
string str;
cout << "请输入一个分数运算表达式,支持加减。" << endl;
cin >> str;
int n = str.length();
char *cal = new char[n];
strcpy(cal, str.c_str());//.c_str()返回一个临时指针
//注意!VS10以上不支持strcpy函数,不安全,有两种解决方法
...
}
Rational r1;
Rational r;
int u = 0, v = 0;
for (int i = 0; i <= n; i++)
{
if (i == n||cal[i] == '+' || cal[i] == '-')
{
v = i;
r1 = getRational(u, v, cal);
r = r + r1; //每次的r1都不同,创建有理数r代表sum,储存运算结果。
u = i + 1;
}
}
Rational getRational(int u, int v,char *cal)//u:被截取的字符串开头
//v:被截取的字符串末尾 形如2/3
{
Rational r1;
for (int i = u; i < v; i++)
{
if (cal[i] == '/')
{
r1.set(getNum(u, i, cal),getNum(i + 1, v ,cal));
}
}
return r1;
}
int getNum(int u,int v,char *cal)
{
int ans = 0;
for (int i = u; i < v; i++)
{
ans = ans * 10; //进位
ans = ans+cal[i]-'0';//字符型不是整型,要存数字,需要减'0'
}
return ans;
#include
#include
#include
using namespace std;
#pragma warning(disable:4996)
class Rational
{
static int Objcount;
private:
int num, den;//分子,分母
public:
Rational(int n = 0, int m = 1):num(n),den(m)//初始分子分母 默认构造函数
{
//cout << "Count" << endl;
Objcount++;
}
~Rational()
{
//cout << "Discount" << endl;
Objcount--;
}
void set(int x, int y)//设定分子分母
{
num = x;
den = y;
}
void print() const//输出x/y
{
cout << num << "/" << den << endl;
}
void simple()//化简
{
int temp;
int x = num, y = den;
while (temp = x % y)//辗转相除法
{
x = y;
y = temp;
}
den /= y;
num /= y;
if (den < 0) {
den = -den;
num = -num;
}
}
public:
Rational operator+(const Rational &r2) const
{
Rational r;
r.num = this->num*r2.den + r2.num*this->den;
r.den = this->den*r2.den;
r.simple();
return r;
}
Rational operator-(const Rational &r2) const
{
Rational r;
r.num = this->num*r2.den - r2.num*this->den;
r.den = this->den*r2.den;
r.simple();
return r;
}
Rational operator*(const Rational &r2) const
{
Rational r;
r.num = this->num*r2.num;
r.den = this->den*r2.den;
r.simple();
return r;
}
Rational operator/(const Rational &r2) const
{
Rational r;
if (r2.num == 0 || r2.den == 0)
{
cout << "分母不得为0" << endl;
}
else {
r.num = this->num*r2.den;
r.den = r2.num*this->den;
}
r.simple();
return r;
}
};
int Rational::Objcount = 0;
int getNum(int u,int v,char *cal)
{
int ans = 0;
for (int i = u; i < v; i++)
{
ans = ans * 10;
ans = ans+cal[i]-'0';
}
return ans;
}
Rational getRational(int u, int v,char *cal)//u:被截取的字符串开头 v:被截取的字符串末尾 形如2/3
{
Rational r1;
for (int i = u; i < v; i++)
{
if (cal[i] == '/')
{
r1.set(getNum(u, i, cal),getNum(i + 1, v ,cal));
}
}
return r1;
}
int main()
{
Rational r;
Rational r1;
string str;
cout << "请输入一个分数运算表达式,支持加减。" << endl;
cin >> str;
int n = str.length();
char *cal = new char[n];
strcpy(cal, str.c_str());//.c_str()返回一个临时指针
bool index = true;
if (cal[0] == '-')
index = false;
int u = 0, v = 0;
for (int i = 0; i <= n; i++)
{
if (i == n||cal[i] == '+' || cal[i] == '-')
{
v = i;
r1 = getRational(u, v, cal);
if (index == true) {
r = r + r1;
}
else r = r - r1;
index = ((cal[i] == '+')?true : false);
u = i + 1;
}
}
cout << "运算结果为:" << endl;
r.print();
getchar();
getchar();
return 0;
}
vs准备弃用strcpy的,安全性较低,所以微软提供了strcpy_s来代替,如果想继续使用strcpy的,main前面加上
#pragma warning(disable:4996)
strcpy_s是安全用法,要指定拷贝的长度
strcpy_s(p, 20, "hello world");
参考文章:c++ 中 char 与 string 之间的相互转换问题
最后,感觉我这个非常小白友好了,写这么详细真的不容易嗷,感谢大家阅读,鞠躬下台!