类 和 C++ 封装 (OOP)
Classes and encapsulation in C++ (OOP)
我们现在在我的课程中使用 OOP 在 CPP 中做 classes,我对它们有点迷茫。我了解整个过程 - 我想,但我似乎无法做到正确。我对此很陌生,所以请放轻松。
基本上,此分配任务的整个前提是创建以下变量和 classes:
- 创建一个 class 命名的三角形
- 封装 a、b、c - 三角形的边长
- bool Set (双 aa, 双 bb, 双 cc); - 设置值和 returns true 或 false 取决于基于这些长度的三角形是否可能
- double Perim(); - 计算三角形的周长。
- 双倍面积(); - 计算三角形面积。
- bool isRect(); - 检查这是否是直角三角形。
我希望这是有道理的?
这是我目前的情况:
main.cpp 文件(远未完成,这只是目前的占位符):
#include <iostream>
#include "triangle.h"
using namespace std;
int main() {
Triangle t;
int aa, bb, cc;
cout <<"Triangle side 1 - " <<endl;
cin >> aa <<endl;
cout <<"Triangle side 2 - ";
cin >> bb <<endl;
cout <<"Triangle side 3 - ";
cin >> cc <<endl;
return 0;
}
Triangle.h 文件:
#include <iostream>
#include "Triangle.cpp"
using namespace std;
#ifndef TRIANGLE_H
#define TRIANGLE_H
class Triangle(){
private:
double a;
double b;
double c;
double Perim();
double Area();
bool IsRect();
public:
Triangle();
Triangle(int, int, int);
bool set(double aa, double bb, double cc);
};
#endif
和Triangle.cpp文件(用公式计算一切)
#include "Triangle.h"
Triangle::Triangle(){
a = b = c = 0;
Perim = 0.0;
Area = 0.0;
}
Triangle::Triangle(int aa, int bb, int cc){
}
double Triangle::Perim(){
return a + b + c;
}
double Triangle::Area(){
s = (a+b+c)/2;
Area = SQRT(s(s-a)(s-b)(s-c));
return Area;
}
bool Triangle::isRect(){
return (((a*a) + (b*b)) == (c*c)) ? true : false; //---checks if this is a right angle triangle, and should return true or false.
}
bool Triangle::Set(double aa, double bb, double cc){
a = aa;
b = bb;
c = cc;
if (a + b > c && a + c > b && b + c > a){//if one of these criteria is false, then a triangle is not possible.
return cout << "A triangle with these parameters is possible.";
}
else{
return cout << "A triangle with these parameters is NOT possible.";
}
}
当然这还远未完成,但我正在努力 link 将所有内容整合在一起。
我试图获取 Main.cpp 文件,当此人输入值时,应该将其传递给其他 cpp 文件并进行计算,一旦完成,return 通过对象将值传递给 Main cpp (尚未创建)。
如果这有意义?
几个小时以来,我一直在努力思考这个问题,但我似乎无法做到正确,希望这里有人能指出我正确的方向吗?
在此先感谢您,对于乱七八糟的代码深表歉意..
这是一个可以接受的开始。但是这里有几个问题。
让我们先看一下header:
您需要删除 cpp 文件的 #include
。您应该只在 header 中包含 header。并且最好将自己限制在 header 中的代码真正需要的 header 中。例如,class 定义并不真正需要 <iostream>
。因此,将此包含移动到需要它的 cpp 中。
你还需要删除 using
子句:这应该在 cpp 中使用,而不是在 headers 中,因为它会导致包含 header 的文件忽略导入了另一个名称空间,这可能会在以后产生冲突(在较大的项目中)。关于如何使用 headers here.
的更多建议
那么 Triangle
是 class 而不是函数。所以你必须用 class Triangle { ... };
而不是 class Triangle() {...};
来定义它
我还希望以下成员函数是 public:
double Perim(); // These are definitively of interest for users of Triangles
double Area();
bool IsRect();
现在开始实施
首先要避免成员变量和成员函数的混淆:
Triangle::Triangle(){
a = b = c = 0;
// Perim = 0.0; // NO !! Perim() is a function, you cannot assign a value to it
// Area = 0.0;
}
然后你需要声明你使用的变量:
double Triangle::Area(){
auto s = (a+b+c)/2; // because s deosn't exist
return std::sqrt(s*(s-a)*(s-b)*(s-c)); // sqrt requires include <cmaths>
// multiplication must be explicit.
}
然后你可以 return 一个布尔表达式。无需更明确:
bool Triangle::isRect(){
return ((a*a) + (b*b)) == (c*c);
}
最后,您的 set 函数需要修改:您必须 return true
或 false
。你最好不要在 return 语句中使用 cout
,而是在 returning 之前。最后但并非最不重要的是,您必须在分配成员变量之前执行有效性测试。
终于到了 main()
输入流和输出流是不同的东西。所以不要在 cin !
上尝试 <<endl
输入成功后,可以t.set(...)
使用用户输入的值来改变t。
如果t.set(...)
return为真,您可以显示函数的结果。例如:
cout << "Area: " << t.Area()<<endl;
如果结果为假,那么最好告诉用户你不能再用这样的三角形做任何事情了。
我想你知道如何编译 main.cpp 和 triangle.cpp 一起编译。
1) 包括 header 个文件,而不是 cpp 文件!
删除:
#include "Triangle.cpp"
原因:
cpp 文件是被编译的。它们依赖于 header 个文件,而不是相反。
headers 可以依赖于其他 headers,这实际上经常发生。但他们不应该依赖于 cpp 文件。
2) 将 headers 包装在 ifndef/define 块中。
移动:
#ifndef TRIANGLE_H
和 #define TRIANGLE_H
到文件的顶部。
从风格上讲,您可能想在 ifndef/define 块上方而不是
块下方评论文件的作用
示例:
// This is the triangle header file! Here is where I describe it.
#ifndef TRIANGLE_H
#define TRIANGLE_H
// TODO: Put the includes here
// TODO: Put your class here
#endif
原因:
在 C++ 中,您可以将行:#include <iostream>
视为 copy-pasting 覆盖该行的 iostream 文件的内容。
因此,当您在 main.cpp 和 triangle.cpp 中 #include "triangle.h" 时,您是在描述三角形 class 的外观
到每个 cpp 文件。 main.cpp 需要知道才能创建和使用三角形,triangle.cpp 需要知道才能创建和使用三角形
正确实现Triangle中的功能。
如果包含一个文件两次,则该文件实际上是 copy-pasted 两次。通常是 re-defining 它的内容,
将是一个编译错误。 ifndef/define 块所做的是说“嘿,如果 TRIANGLE_H 尚未定义,这是第一个
它被包含的时间。因此,继续定义 TRIANGLE_H 以及所有文件内容。下次包含triangle.h
我们会看到 TRIANGLE_H 已经定义了,我们直接跳过 header 的内容,而不是 re-defining
三角是.
我建议你把它移到顶部的原因是你包括了 iostream,triangle.cpp(我们已经讨论过那部分)和
每次包含 triangle.h 时都使用命名空间标准。没必要。
3) 修复三角形class声明
变化:
class Triangle(){
至
class Triangle {
原因:
你很接近!但是您将声明构造函数的语法与声明 class.
的语法混淆了
4) 创建您打算在 Triangle 之外使用的函数 public.
移动:
上面的 "public:" 访问说明符 double Perim();
原因:
如果您想从 main.cpp(或从除三角形 class 内部以外的任何地方调用方法:Perim、Area 和 IsRect!)
你需要这些方法是 public.
您可能很想制作所有内容 public,但这是一种糟糕的形式 - 您的导师特别说过 "encapsulate a, b, c"。
你可以 google 封装设计模式来理解为什么。这里就不展开了。
5) 填写你的三角形构造函数
查看方法:
Triangle::Triangle(int aa, int bb, int cc)
变化:
在 header 文件中,您没有给出三个参数的名称。这不是错误,但很奇怪。
您可能应该给这些参数命名。
在您的另一个构造函数(没有参数的构造函数)中,您表明您知道如何为 a、b 和 c 赋值!
继续在这个方法中给它们赋值。
考虑一下你是否真的想使用int
作为参数的类型。
您将边长存储为双精度,但将它们作为整数提供给构造函数。这不是错误,但很奇怪。
6) 修复Set方法的body
查看方法:
Triangle::Set(double aa, double bb, double cc)
注:
记住方法就是交换。你给它提供它的参数,它做一些工作并且它 return 给你一些值(除非它 'returns' 无效)。
您的方法要求 3 个双打,并且 returning 一个布尔值。如果您 return 是一个布尔值,则表示您同意返回真值或假值。
在这里你试图 return 任何 cout << "..."; return秒。这听起来不像你想要的(即使它编译)。你不关心 cout return 是什么,对吧?
变化:
不要从这个函数中计算出任何东西。如果三角形可能,则 return 为 true,如果不可能,则为 false。
你可以从调用 Set 的地方计算出任何你想要的东西。
7) 封装a、b、c。
您已将 b 和 c 设为私有。这是封装的一半。
你的导师可能要你为三角形中的这些变量写一个getter和setter。
示例:
double Triangle::GetA() {
return a;
}
void Triangle::SetA(double aa) {
a = aa;
}
我会把它作为运动量来做剩下的事情。
8) 在 main.cpp
中使用三角形
如果你使用局部变量 t
,你可以像这样调用它的函数:
t.SetA(1.1);
cout << t.GetA() << endl; // will print 1.1
double theValueOfA; // local variable
theValueOfA = t.GetA(); // store the result of GetA in our new variable
cout << theValueOfA << endl; // will also print 1.1
根据这些信息,在完成对 cin 的调用以获取用户输入后,执行以下操作:
- 创建一个布尔变量来存储集合的结果,我们稍后会用到它
- 调用 t 上的 Set 函数,提供用户输入的 3 个长度。将 Set 的 return 存储在我们的布尔值中。
- 写一个if语句。检查该布尔值是否为真。如果是,打印"A triangle with these parameters is possible.",否则打印"A triangle with these parameters is NOT possible."
- 如果您的任务要求您做任何其他事情或任何不同的事情,例如打印 Perim、Area 和 isRect 的结果,那就去做吧!
我们现在在我的课程中使用 OOP 在 CPP 中做 classes,我对它们有点迷茫。我了解整个过程 - 我想,但我似乎无法做到正确。我对此很陌生,所以请放轻松。
基本上,此分配任务的整个前提是创建以下变量和 classes:
- 创建一个 class 命名的三角形
- 封装 a、b、c - 三角形的边长
- bool Set (双 aa, 双 bb, 双 cc); - 设置值和 returns true 或 false 取决于基于这些长度的三角形是否可能
- double Perim(); - 计算三角形的周长。
- 双倍面积(); - 计算三角形面积。
- bool isRect(); - 检查这是否是直角三角形。
我希望这是有道理的?
这是我目前的情况:
main.cpp 文件(远未完成,这只是目前的占位符):
#include <iostream>
#include "triangle.h"
using namespace std;
int main() {
Triangle t;
int aa, bb, cc;
cout <<"Triangle side 1 - " <<endl;
cin >> aa <<endl;
cout <<"Triangle side 2 - ";
cin >> bb <<endl;
cout <<"Triangle side 3 - ";
cin >> cc <<endl;
return 0;
}
Triangle.h 文件:
#include <iostream>
#include "Triangle.cpp"
using namespace std;
#ifndef TRIANGLE_H
#define TRIANGLE_H
class Triangle(){
private:
double a;
double b;
double c;
double Perim();
double Area();
bool IsRect();
public:
Triangle();
Triangle(int, int, int);
bool set(double aa, double bb, double cc);
};
#endif
和Triangle.cpp文件(用公式计算一切)
#include "Triangle.h"
Triangle::Triangle(){
a = b = c = 0;
Perim = 0.0;
Area = 0.0;
}
Triangle::Triangle(int aa, int bb, int cc){
}
double Triangle::Perim(){
return a + b + c;
}
double Triangle::Area(){
s = (a+b+c)/2;
Area = SQRT(s(s-a)(s-b)(s-c));
return Area;
}
bool Triangle::isRect(){
return (((a*a) + (b*b)) == (c*c)) ? true : false; //---checks if this is a right angle triangle, and should return true or false.
}
bool Triangle::Set(double aa, double bb, double cc){
a = aa;
b = bb;
c = cc;
if (a + b > c && a + c > b && b + c > a){//if one of these criteria is false, then a triangle is not possible.
return cout << "A triangle with these parameters is possible.";
}
else{
return cout << "A triangle with these parameters is NOT possible.";
}
}
当然这还远未完成,但我正在努力 link 将所有内容整合在一起。 我试图获取 Main.cpp 文件,当此人输入值时,应该将其传递给其他 cpp 文件并进行计算,一旦完成,return 通过对象将值传递给 Main cpp (尚未创建)。 如果这有意义?
几个小时以来,我一直在努力思考这个问题,但我似乎无法做到正确,希望这里有人能指出我正确的方向吗?
在此先感谢您,对于乱七八糟的代码深表歉意..
这是一个可以接受的开始。但是这里有几个问题。
让我们先看一下header:
您需要删除 cpp 文件的 #include
。您应该只在 header 中包含 header。并且最好将自己限制在 header 中的代码真正需要的 header 中。例如,class 定义并不真正需要 <iostream>
。因此,将此包含移动到需要它的 cpp 中。
你还需要删除 using
子句:这应该在 cpp 中使用,而不是在 headers 中,因为它会导致包含 header 的文件忽略导入了另一个名称空间,这可能会在以后产生冲突(在较大的项目中)。关于如何使用 headers here.
那么 Triangle
是 class 而不是函数。所以你必须用 class Triangle { ... };
而不是 class Triangle() {...};
我还希望以下成员函数是 public:
double Perim(); // These are definitively of interest for users of Triangles
double Area();
bool IsRect();
现在开始实施
首先要避免成员变量和成员函数的混淆:
Triangle::Triangle(){
a = b = c = 0;
// Perim = 0.0; // NO !! Perim() is a function, you cannot assign a value to it
// Area = 0.0;
}
然后你需要声明你使用的变量:
double Triangle::Area(){
auto s = (a+b+c)/2; // because s deosn't exist
return std::sqrt(s*(s-a)*(s-b)*(s-c)); // sqrt requires include <cmaths>
// multiplication must be explicit.
}
然后你可以 return 一个布尔表达式。无需更明确:
bool Triangle::isRect(){
return ((a*a) + (b*b)) == (c*c);
}
最后,您的 set 函数需要修改:您必须 return true
或 false
。你最好不要在 return 语句中使用 cout
,而是在 returning 之前。最后但并非最不重要的是,您必须在分配成员变量之前执行有效性测试。
终于到了 main()
输入流和输出流是不同的东西。所以不要在 cin !
上尝试<<endl
输入成功后,可以t.set(...)
使用用户输入的值来改变t。
如果t.set(...)
return为真,您可以显示函数的结果。例如:
cout << "Area: " << t.Area()<<endl;
如果结果为假,那么最好告诉用户你不能再用这样的三角形做任何事情了。
我想你知道如何编译 main.cpp 和 triangle.cpp 一起编译。
1) 包括 header 个文件,而不是 cpp 文件!
删除:
#include "Triangle.cpp"
原因:
cpp 文件是被编译的。它们依赖于 header 个文件,而不是相反。 headers 可以依赖于其他 headers,这实际上经常发生。但他们不应该依赖于 cpp 文件。
2) 将 headers 包装在 ifndef/define 块中。
移动:
#ifndef TRIANGLE_H
和 #define TRIANGLE_H
到文件的顶部。
从风格上讲,您可能想在 ifndef/define 块上方而不是
示例:
// This is the triangle header file! Here is where I describe it.
#ifndef TRIANGLE_H
#define TRIANGLE_H
// TODO: Put the includes here
// TODO: Put your class here
#endif
原因:
在 C++ 中,您可以将行:#include <iostream>
视为 copy-pasting 覆盖该行的 iostream 文件的内容。
因此,当您在 main.cpp 和 triangle.cpp 中 #include "triangle.h" 时,您是在描述三角形 class 的外观
到每个 cpp 文件。 main.cpp 需要知道才能创建和使用三角形,triangle.cpp 需要知道才能创建和使用三角形
正确实现Triangle中的功能。
如果包含一个文件两次,则该文件实际上是 copy-pasted 两次。通常是 re-defining 它的内容, 将是一个编译错误。 ifndef/define 块所做的是说“嘿,如果 TRIANGLE_H 尚未定义,这是第一个 它被包含的时间。因此,继续定义 TRIANGLE_H 以及所有文件内容。下次包含triangle.h 我们会看到 TRIANGLE_H 已经定义了,我们直接跳过 header 的内容,而不是 re-defining 三角是.
我建议你把它移到顶部的原因是你包括了 iostream,triangle.cpp(我们已经讨论过那部分)和 每次包含 triangle.h 时都使用命名空间标准。没必要。
3) 修复三角形class声明
变化:
class Triangle(){
至
class Triangle {
原因:
你很接近!但是您将声明构造函数的语法与声明 class.
的语法混淆了4) 创建您打算在 Triangle 之外使用的函数 public.
移动:
上面的 "public:" 访问说明符 double Perim();
原因:
如果您想从 main.cpp(或从除三角形 class 内部以外的任何地方调用方法:Perim、Area 和 IsRect!) 你需要这些方法是 public.
您可能很想制作所有内容 public,但这是一种糟糕的形式 - 您的导师特别说过 "encapsulate a, b, c"。 你可以 google 封装设计模式来理解为什么。这里就不展开了。
5) 填写你的三角形构造函数
查看方法:
Triangle::Triangle(int aa, int bb, int cc)
变化:
在 header 文件中,您没有给出三个参数的名称。这不是错误,但很奇怪。 您可能应该给这些参数命名。
在您的另一个构造函数(没有参数的构造函数)中,您表明您知道如何为 a、b 和 c 赋值! 继续在这个方法中给它们赋值。
考虑一下你是否真的想使用
int
作为参数的类型。 您将边长存储为双精度,但将它们作为整数提供给构造函数。这不是错误,但很奇怪。
6) 修复Set方法的body
查看方法:
Triangle::Set(double aa, double bb, double cc)
注:
记住方法就是交换。你给它提供它的参数,它做一些工作并且它 return 给你一些值(除非它 'returns' 无效)。 您的方法要求 3 个双打,并且 returning 一个布尔值。如果您 return 是一个布尔值,则表示您同意返回真值或假值。 在这里你试图 return 任何 cout << "..."; return秒。这听起来不像你想要的(即使它编译)。你不关心 cout return 是什么,对吧?
变化:
不要从这个函数中计算出任何东西。如果三角形可能,则 return 为 true,如果不可能,则为 false。 你可以从调用 Set 的地方计算出任何你想要的东西。
7) 封装a、b、c。
您已将 b 和 c 设为私有。这是封装的一半。 你的导师可能要你为三角形中的这些变量写一个getter和setter。
示例:
double Triangle::GetA() {
return a;
}
void Triangle::SetA(double aa) {
a = aa;
}
我会把它作为运动量来做剩下的事情。
8) 在 main.cpp
中使用三角形如果你使用局部变量 t
,你可以像这样调用它的函数:
t.SetA(1.1);
cout << t.GetA() << endl; // will print 1.1
double theValueOfA; // local variable
theValueOfA = t.GetA(); // store the result of GetA in our new variable
cout << theValueOfA << endl; // will also print 1.1
根据这些信息,在完成对 cin 的调用以获取用户输入后,执行以下操作:
- 创建一个布尔变量来存储集合的结果,我们稍后会用到它
- 调用 t 上的 Set 函数,提供用户输入的 3 个长度。将 Set 的 return 存储在我们的布尔值中。
- 写一个if语句。检查该布尔值是否为真。如果是,打印"A triangle with these parameters is possible.",否则打印"A triangle with these parameters is NOT possible."
- 如果您的任务要求您做任何其他事情或任何不同的事情,例如打印 Perim、Area 和 isRect 的结果,那就去做吧!