我试着去理解OOP,想知道我的函数是否完成得正确
Trying to get my head around OOP and wondering if my functions are done correctly
我正在制作一个简单的小型计算器应用程序,我想知道如何最好地将封装和抽象结合到代码中,使其整洁有序。
这是如何使用以下代码处理这些方法的理想方法吗?我不确定它是否有点过火,但在我看来,它让它非常可读和整洁。
还有一条规则是,如果你在重复代码,它就属于一个函数吗?
提前谢谢。
// Test ApplicationV2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
int getNumber();
char getOperation();
int additionResult(int& val1, int& val2);
int multiplyResult(int& val1, int& val2);
int divisionResult(int& val1, int& val2);
int main()
{
int val1, val2;
char operation;
std::cout << "Enter the first value.";
val1 = getNumber();
std::cout << "Enter operation";
operation = getOperation();
std::cout << "Enter second number.";
val2 = getNumber();
switch (operation)
{
case '+':
std::cout << additionResult(val1, val2);
break;
case '*':
std::cout << multiplyResult(val1, val2);
break;
case '/':
std::cout << divisionResult(val1, val2);
break;
default:
std::cout << "Please enter sufficient data.";
}
//Keep window open
std::string barn;
std::cin >> barn;
return 0;
}
int getNumber()
{
int x;
std::cin >> x;
return x;
}
char getOperation()
{
char op;
std::cin >> op;
return op;
}
int additionResult(int& val1, int& val2)
{
return (val1 + val2);
}
int multiplyResult(int& val1, int& val2)
{
return (val1 * val2);
}
int divisionResult(int& val1, int& val2)
{
return (val1 / val2);
}
如上所述,您的程序不使用OOP(除非std::cout
和std::cin
是对象)。如果您的目标是探索使用OOP作为构建代码的一种方式,那么可以考虑:
#include <iostream>
class Calculation
{
public:
Calculation(int first, int second)
: first_(first), second_(second)
{ }
int add() { return first_ + second_; }
int multiply() { return first_ * second_; }
int divide() { return first_ / second_; }
private:
int first_, second_;
};
int main()
{
int val1, val2;
char operation;
if (std::cout << "Enter the first value: " &&
std::cin >> val1 &&
std::cout << "Enter operation: " &&
std::cin >> operation &&
std::cout << "Enter the second value: " &&
std::cin >> val2)
{
Calculation calc(val1, val2);
switch (operation)
{
case '+':
std::cout << calc.add() << 'n';
break;
case '*':
std::cout << calc.multiply() << 'n';
break;
case '/':
std::cout << calc.divide() << 'n';
break;
default:
std::cerr << "Invalid operator: must be +, * or /.n";
}
}
else
std::cerr << "Error getting inputs.n";
//Keep window open
std::cin.get(); // real operating systems don't need this ;-P
}
注意,Calculation
类是OOP方面,它有private
数据:这是封装。虽然它没有太多抽象——问题太简单了——但你可以说它捕获了构造时的输入和add()
、multiply()
,divide()
调用不必知道所涉及的数据的类型或数量(即,Calculator
可能存储了std::vector<int>
或double
,并且switch
语句的代码不需要更改,前提是这三个函数的返回类型存在operator<<
重载)。
您可以通过在Calculation
中拥有一个成员函数来获得更多的抽象,比如:
int do_operation(char c) const
{
switch (c)
{
case '+': return first_ + second_;
case '*': return first_ * second_;
case '/': return first_ / second_;
default: throw std::runtime_error("invalid operator character");
}
}
然后main
的使用可以尝试一个操作,看看它是否受支持,而不知道Calculation
:提供的确切操作
Calculation calc(val1, val2);
try
{
std::cout << calc.do_operation(operation) << 'n';
}
catch (const std::exception& e)
{
std::cerr << "oops - caught exception: " << e.what() << 'n';
}
这似乎没什么大不了的,但这意味着,如果您有很多地方使用Calculation
对象,那么当您想要支持另一个操作时,就不必全部更新它们。缺点是CCD_ 17的返回类型对于所有类型的操作都必须相同,因此,您不能决定"嘿,我将使用除法返回double
,而不是四舍五入到int
",只需更改divide()
的返回类型即可(但您可以让它们都返回double
-在这种简单的情况下不会造成太大伤害,更普遍的情况是,您可以使用"variant"返回一组类型中的任何一个,但客户端代码更难使用它们)。
还有一条规则是,如果你在重复代码,它就属于一个函数吗?
差不多。请注意,您没有重复(调用两次)放入函数中的任何代码,但这在更复杂的程序中变得很重要。
作为使用函数分解上述代码的一个示例,请考虑如何将提示和输入组合到可重用函数中:
template <typename T>
bool input(const std::string& prompt, T& value)
{
return std::cout << prompt && std::cin >> value;
}
template <typename T>
意味着上述函数可以与任何类型的value
一起工作。使用这个input
功能,提示和输入操作可以重写为:
if (input("Enter the first value: ", val1) &&
input("Enter operation: ", operation) &&
input("Enter the second value: ", val2))
{
这是否更好取决于读者是否能够猜测input
做得足够好,不需要去研究input
函数,但它被重复使用的频率越高,即使这样的努力也越有可能是值得的。
一种可能性是有一个抽象的"二进制运算"类,然后为每个实际运算(加法、减法、乘法、除法)都有派生类。
如果您这样做,您就可以(例如)创建一个包含(指向)二进制操作对象的指针或引用的容器,并在该集合中查找正确的操作。例如:
struct bin_op {
virtual int operator()(int a, int b);
};
struct add : bin_op {
virtual int operator()(int a, int b) { return a + b; }
};
struct sub : bin_op {
virtual int operator()(int a, int b) { return a - b; }
};
// mul and div similarly.
std::map<char, bin_op *> ops;
add a;
sub s;
mul m;
div d;
ops['+'] = &a;
ops['-'] = &s;
ops['*'] = &m;
ops['/'] = &d;
然后要执行操作,您可以执行以下操作:
auto b = ops.find(operation);
if (b != ops.end())
std::cout << b->second(val1, val2);
else
std::cerr << "unknown operation: " << operation << "n";
这样做的一个明显优点是,添加新操作非常简单:定义一个从bin_op
派生的新类来执行新操作,将其实例添加到ops
(带有要触发该操作的字符),然后就可以了。
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 是否有类似std::lower_bound的函数,而不需要排序/分区输入
- 函数作为模板参数,是否对返回类型强制约束
- visual是否可以在c++中创建一个接收无限数量相同类型(或至少相当数量)参数的函数
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 函数是否可以访问传递给main()的参数
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 在这种情况下,java对象是否可以调用本机函数
- 检查函数返回类型是否与STL容器类型值相同
- 根据某个函数是否存在启用模板
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 无论如何,我可以确定构造函数是否存在吗?
- 是否可以将函数导入命名空间,但不能导出它?
- 返回指向对象的指针的函数调用是否为 prvalue?
- 是否可以依赖函数范围的静态变量来执行程序关闭期间调用的方法?
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 是否有任何建议来统一函数类型限定符并简化可恶的函数类型?
- 在函数范围内在堆栈上分配的数组在离开函数时是否总是被释放?
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本