自订资料型态可以是
虽然通常不太会写到自订资料型态
但是如果是在大型的专案中,常常会利用自订资料型态来提高效率。
今天有 点A、B在一个二维平面(Cartesian coordinate system)上,我们要计算 vector AB,并 print out。
void vector(int x1, int y1, int x2, int y2, int& rx, int& ry) // call by reference
{
rx = x2 - x1;
ry = y2 - y1;
}
int main()
{
int x1 = 0, x2 = 0;
int y1 = 0, y2 = 0;
int rx = 0, ry = 0; // 用来存回传质
vector(x1, y1, x2, y2, rx, ry);
cout << rx << " " << ry << "\n";
return 0;
}
虽然我们也可以这样写,但是因为变数太多,如果有更多的变数(x3, y3)的话就会搞得很乱。
所以在这边,我们可能很想要把(x1, y1) (x2, y2)...等等的不同组 x y 把它每一个做成一个点,这时候就可以用上 struct 了!
功能:
透过struct 可以让我们把不同的资料型态,合成一种资料型态。用中文来说,就是把很多东西集合在一起,形成一个结构,这样以後就可以自由的取用他了。(struct = structure)
宣告:
struct Point(定义一个新的型态)
{
int x; // x属性
int y; // y属性
};
宣告之後,我们就可以使用他了!
我们就可以这样写:
Point vector(Point A, Point B)
{
Point vecXY;
vecXY.x = B.x - A.x;
vecXY.y = B.y - A.y;
return vecXY;
}
int main()
{
Point a = {0, 0}, b = {10, 20};
Point vecAB = vector(a, b);
cout << vecAB.x << " ";
cout << vecAB.y << "\n";
return 0;
}
可以把struct 想像成把一堆东西装进去一个资料夹,然後再帮他们编号,等到我们要使用的时候就会使用 . 把他们取出来,後面的 x y 就是他们的编号
Definition of struct:
struct Struct name
{
type1 field1; // member variable
type2 field2;
type3 field3;
//more field
};
struct variable declaration:
struct name variable name;
e.g.
Point A;
Point B, C, thisIsAPoint;
Point staticPointArray[10];
Point* pointPtr = thisIsAPoint;
Point* dynamicPointArray = new Point[10];
Accessing struct attributes:
struct name . attribute name
a.b.c
// c 是 b 的 attribute
// b 是 a 的 attribute
struct assignment:
struct Point {
int x;
int y;
int z;
};
int main()
{
Point A[100];
for (int i = 0; i < 50; i++)
A[i] = {i};
for (int i = 0; i < 100; i++)
cout << A[i].x << " " << A[i].y
<< " " << A[i].z << "\n";
return 0;
}
像这个程序,我们指派了前 50 个东西的 value,但是後面没有指派。
结果会显示(x, y, z):
前五十个: (i, 0, 0)
後五十个:开始出现不知道哪来的数字
struct Point
{
int x;
int y;
};
void reflect(Point& a)
{
int temp = a.x;
a.x = a.y;
a.y = temp;
}
int main(int argc, char const *argv[])
{
Point a = {10, 20};
cout << a.x << " " << a.y << endl;
reflect(a);
cout << a.x << " " << a.y << endl;
return 0;
}
Memory allocation for struct:
如果宣告一个 struct,记忆体是怎配置的呢?
struct Point
{
int x;
int y;
};
int main()
{
Point a[3];
cout << sizeof(Point) << " " << sizeof(a) << "\n";
cout << &a << "\n";
for (int i = 0; i< 3; i ++)
cout << &a[i] << " " << &a[i].x << " " << &a[i].y << "\n";
Point* b = new Point[3];
cout << sizeof(b) << "\n";
delete [] b;
b = nullptr;
return 0;
}
透过这个程序可以知道:
Definition of typedef:
typeof old type new type;
简单说就是把 帮 old type 取一个新的名字,你用 old 或是 new 的时候都可以叫出来。除了可以方便阅读以外,它还有下面这种好处。
Application:
如果今天我们有一个程序,写了很多重复的东西,像是 3.14,理想气体常数, etc。我们这时候可能会用 const
来宣告它(e.g., pi, G),且让它不能被更改。
如果今天我们要计算汇率,可能会写一个程序:
double us = 0;
double nt = 0;
cin >> us;
nt = us * 28;
cout << us << " " << nt;
但是问题来的,如果今天要把 double 全部改成 float,而且如果你今天这个程序是在计算全世界货币的汇率,这该怎麽办?
这时候 typedef 就派上用场了:
typedef double Dollar;
Dollar us = 0;
Dollar nt = 0;
cin >> us;
nt = us * 28;
cout << us << " " << nt;
如果这个时候你想要改成 float ,直接改成 typedef float Dollar
就好了!
Type life cycle
可以在任何地方宣告 typedef,但是它只会存活到那一个 block 而已:struct 也是。多半大家都会写在 using namespace std; 的後面。
global type; local variable
Application:
把 typedef 和 struct 混合在一起用
刚刚我们写过
Point a = {0, 0};
Point b = {10, 20};
Point vecAB = vector(a, b);
但是实际上,如果以 Point 来表示 vector 其实蛮奇怪的,所以我们可以这样做:
typedef Point Vector;
这样我们就可以改成 :
Point a = {0, 0};
Point b = {10, 20};
Vector vecAB = vector(a, b);
这样就会更好理解程序了。
在很多 C++ stand library里面的函数,会提供被typedef 定义,新的 data type。
clock()
功能:计算从开始 program 的时候,经过了多少 system clock
application:
#include<iostream>
#include<ctime>
using namespace std;
int main(int argc, char const *argv[])
{
*clock_t sTime* = clock();
for (int i = 0; i < 1000000000; ++i)
;
*clock_t eTime* = clock();
cout << sTime << " " << eTime << endl;
return 0;
}
这时候你可能会疑惑: what is clock_t?
事实上,clock()
回传的type 被称为 clock_t。在 library 里面已经宣告了 typedef long int clock_t;
,所以说 clock_t 就是 long int,所以上面的程序如果改成用 long int 宣告:long int sTime = clock();
也是行的。原因是因为跟刚刚的常数一样,像是 pi 或是其他常数,如果你某一天想要改成 long long int,这时候我们只需要改宣告 clock_t 的那一行就行了。
但是上面程序 cout 的会是 system second的欸,那我们要怎麽知道到底是几秒。其实就把两个时间相减,再除以 CLOCKS_PER_SEC这个 const 就好了!我的电脑跑出来是 1.59583。
cout << static_cast<double>(eTime - sTime) / CLOCKS_PER_SEC;
strlen()
size_t strlen(const char* str);
我们上面宣告的:
struct Point
{
int x;
int y
};
这两个叫做 member variable 或者是 attribute(属性)
那如我我们想对 Point 做一些事情要怎麽办?
像是我们要计算这两个点的距离。
double distOri (Point p)
{
double dist = sqrt(pow(p.x, 2) + pow(p.y, 2));
return dist;
}
// remember to include <cmath>
是可以这样写的,但是也可以直接放在 struct 里面:
struct
{
int x;
int y;
double distOri()
{
return sqrt(pow(x, 2) + pow(y, 2))
}
}
可以把它像是这个机器一样,萤幕显示参数 x y,上面有一个摇杆可以处理这两个参数。(小杰老师画的xD 很可爱)
如果要呼叫这个函数的话:
int main()
{
Point a = {3, 5};
cout << a.distOri();
return 0;
}
这时候可以把 a 想像成一个机器,按下按钮(.distOri()
)就可以处理这些事情。
而 struct 里面的函数,也可以写成像是一般函数的 header and body 一样。
struct
{
int x;
int y;
double distOri();
}
double Point::disOri()
{
return sqrt(pow(x, 2), pow(y, 2));
}
使用 member function 的优点就是,如果今天有很多个function,这样使用 member function 的时候,程序可以比较好理解,且在 debug 或是美观而言,也会比较好看。
Another example:
struct Point
{
int x;
int y;
};
void reflect(Point& a)
{
int temp = a.x;
a.x = a.y;
a.y = temp;
}
void print(Point a)
{
cout << a.x << " " << a.y << "\n";
}
int main(int argc, char const *argv[])
{
Point a = {10, 20};
Point b = {5, 2};
print(a);
print(b);
reflect(a);
print(a);
print(b);
return 0;
}
我们刚刚的 reflect ,也可以把他们改成 member function:
struct Point
{
int x;
int y;
void reflect();
void print();
};
void Point::reflect()
{
int temp = x;
x = y;
y = temp;
}
void Point::print()
{
cout << x << " " << y << "\n";
}
int main(int argc, char const *argv[])
{
Point a = {10, 20};
Point b = {5, 2};
a.print();
b.print();
a.reflect();
a.print();
b.print();
return 0;
}
虽然说两者跑出来的东西其实是一样的,但是如果有很多同一类的(对差不多的东西做某些事) function,就很推荐使用这个。
另外,用 member function 也是一个模组化很好的方式。
Random numbers:
int rand():
回传:它会回传一个从 0 到 RAND_MAX 中随机一个 integer。
但是它回传的 的是 "Pseudo-random" integer,也就是说他们虽然长的像是 random,但是实际上是有一个公式的:e.g., ri = (941324314 * r_(i-1) + 18293084) mod 32767
他是拿上一个 number 去做下一个乱数。
所以我们唯一能决定的就是 random number seed (第一个),这时候就要使用
void srand(unsigned int)
那我们要怎保证每次传给 srand()
的数字都不一样?很多时候我们会使用 time(nullptr) 当作他的 argument。
header:
time_t time(time_t* timer);
回传:从 1970. 1. 1 至今经过了几秒。
使用:(须 include ctime)
time_t t = time(nullptr)
time(&t);
srand(t);
cout << t << "\n";
如果想要让我们的随机数字在某个 range 里面,就可以使用 %
#include<iostream>
#include<ctime>
#include<cstdlib>
using namespace std;
int main(int argc, char const *argv[])
{
srand(time(0));
int rn = 0;
for (int i = 0; i < 10; ++i)
{
rn = ((rand() % 10)) + 100;
//或是也可以: rn = (static_cast<double>(rand() % 501)) / 100;
cout << rn;
}
return 0;
}
如果我们今天想要用 self-defined 的方式制作我们自己的 random number generator (也就是 rand()),其实也是可以的。
//就可以这样写
struct Randomizer
{
int a;
int b;
int c;
int cur;
int rand();
}
int Randomizer::rand()
{
cur = (a * cur + b) + c;
return cur;
}
使用:
int main()
{
Randomizer r1 = {10, 4, 31, 0};
for (int i = 0; i < 10; i++)
cout << r1.rand() << " ";
cout << endl;
Randomizer r2 = {10, 7, 32, 0};
for (int i = 0; i < 10; i++)
cout << r2.rand() << " ";
cout << endl;
return 0;
}
r1 会跑出:4 13 10 11 21 28 5 23 17 19
r2 则会跑出:7 13 9 1 17 17 17 17 17 17
可以了解到,不同的参数会跑出不一样的 random 的结果。
今天学的 struct 终於让我知道,那些 .
到底是从哪里来的了。像是我最近看游戏的教学影片,里面就用了很多 .getPosition
.setPosition
,现在才知道 wow 原来就是这麽一回事!
T0856 Spoof Reporting Message 攻击者欺骗回传报告的内容,为了不让自己的...
阵列(Array)是一种常见的资料结构,常用来处理相同类型的有序资料,并存放在连续的记忆体空间中。但...
Introduce 为了API的安全性,本次跟各位介绍透过JWT Token来帮API做身分验证,简...
这是回顾Go Smart Award的最後一集, 整个比赛的内容也在颁奖典礼举行的同时, 配合在...
我们现在知道,你所传递的讯息,会被包裹起来丢到网路上的节点中转运,直至抵达目的地。那具体会通过怎麽样...