自訂資料型態可以是
雖然通常不太會寫到自訂資料型態
但是如果是在大型的專案中,常常會利用自訂資料型態來提高效率。
今天有 點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 原來就是這麼一回事!