Original article: http://www.4p8.com/eric.brasseur/cppcen.html

C++, C падручнік для карыстальнікаў

Гэты тэкст паказвае, і разглядаюцца асаблівасці і асноўныя прынцыпы C + +. Яна накіравана на вопытных карыстальнікаў C, жадаючых навучыцца C + +. Вы зможаце выказаць свае код, выкарыстоўваючы багаты C + + сінтаксіс і вы зможаце азнаёміцца ??з некаторымі C + + кода.

Хоць паняцце ўведзена і зроблена зручным, гэта не падручнік па аб'ектна-арыентаванага праграмавання. Вы па-ранейшаму павінны навучыцца духу ААП і дэталі яго рэалізацыі ў З + +, для таго, каб быць сапраўдным C + + праграміст.








1.

Існуе новы спосаб # ўключаюць бібліятэкі (стары метад усё яшчэ працуе яшчэ кампілятар скардзіцца). . Ч пашырэнне больш не выкарыстоўваецца, і імёны стандартных бібліятэк C пішуцца пачынаючы з з. Для таго, каб праграмы для выкарыстання гэтых бібліятэк правільна выкарыстаннем імёнаў STD; павінна быць дададзена:


using namespace std;
#include <iostream> // This is a key C++ library
#include <cmath> // The standard C library math.h

int main ()
{
double a;

a = 1.2;
a = sin (a);

cout << a << endl;

return 0;
}


Output
0.932039



Некалькі саветаў для пачаткоўцаў:

Для кампіляцыі гэтай праграмы, увядзiце яго (або скапіяваць і ўставіць) у тэкставым рэдактары (Gedit, KWrite, Кейт, KEdit, У. І., Emacs, нана-, піка, Mcedit, нататнік...), захаваць яго як файл з імем, кажуць test01.cpp (калі вы пачатковец, лепш за ўсё пакласці гэты файл у вашым хатнім каталогу, гэта значыць, напрыклад, / галоўная/Джонс на Unix-падобнай скрынцы).

Для кампіляцыі файла зыходнага кода, увядзіце наступную каманду (у большасці з адкрытым зыходным кодам Unix-падобных каробак) у кансолі або акне тэрмінала:

G + + test01.cpp-O test01

Каб запусціць бінарны выкананы файл test01, што быў падрыхтаваны зборнік (калі не было памылкі), тыпу гэтага:

./test01

Кожны раз, калі вы змяняеце зыходны код test01.cpp файл, вы павінны скампіляваць яго зноў, калі вы хочаце змены павінны быць адлюстраваны ў test01 выкананы файл (тыпу "стрэлка уверх" на клавіятуры, каб нагадаць каманд).





2.

Вы можаце выкарыстоўваць // для тыпу заўвага:


 выкарыстанне імёнаў STD; // Using the standard library namespace.
#include <iostream> // The iostream library is often used.

int main () // The program's main routine.
{
double a; // Declaration of variable a.

a = 456.47;
a = a + a * 21.5/100; // A calculation.

cout << a << endl; // Display the content of a.

return 0; // Program end.
}



Выхад
554.611



(Магчымасць выкарыстоўваць // для тыпу заўваг быў дададзены ў C у C99 і ANSI C 2000 года).





3.

Увод з клавіятуры і вывад на экран можа быць выканана праз суд <<і CIN>>


using namespace std;
#include <iostream>

int main()
{
int a; // a is an integer variable
char s [100]; // s points to a string of max 99 characters

cout << "This is a sample program." << endl;

cout << endl; // Just a line feed (end of line)

cout << "Type your age : ";
cin >> a;

cout << "Type your name: ";
cin >> s;

cout << endl;

cout << "Hello " << s << " you're " << a << " old." << endl;
cout << endl << endl << "Bye!" << endl;

return 0;
}

Выхад
Гэта прыклад праграмы.

Калі ласка, увядзіце свой ??узрост: 12
Калі ласка, увядзіце імя: Эдмонд

Прывітанне Эдманд вы 12 старых.


Пакуль!





4. 

Зменныя могуць быць аб'яўлены ў любым месцы ўнутры кода:


using namespace std;
#include <iostream>

int main ()
{
double a;

cout << "Hello, this is a test program." << endl;

cout << "Type parameter a: ";
cin >> a;

a = (a + 1)/2;

double c;

c = a * 5 + 1;

cout << "c contains  : " << c << endl;

int i, j;

i = 0;
j = i + 1;

cout << "j contains  : " << j << endl;

return 0;
}


Выхад
Добры дзень, гэта тэст праграмы.
Type parameter a: 7
c contains      : 21
j contains      : 1



Можа быць, паспрабаваць выкарыстаць гэтую магчымасць, каб зрабіць ваш зыходны код больш зручным для чытання, а не псаваць яго.
Як і ў C, зменныя могуць быць інкапсуляваць паміж {} блокаў. Тады яны з'яўляюцца лакальнымі па сваім аб'ёме зоны інкапсуляваць паміж {і}. Што б ні здарылася з такімі зменнымі ўнутры інкапсуляваць зона будзе мець ніякага эфекту за межамі зоны:


using namespace std;
#include <iostream>

int main ()
{
double a;

cout << "Type a number: ";
cin >> a;

{
int a = 1;
a = a * 10 + 4;
cout << "Local number: " << a << endl;
}

cout << "You typed: " << a << endl;

return 0;
}


Выхад
Калі ласка, увядзіце лік: 9
Мясцовы нумар: 14
Вы ўвялі: 9





5.

Пераменная можа быць уведзены па разліку з удзелам іншых зменных:


using namespace std;
#include <iostream>

int main ()
{
double a = 12 * 3.25;
double b = a + 1.112;

cout << "a contains: " << a << endl;
cout << "b contains: " << b << endl;

a = a * 2 + b;

double c = a + b * a;

cout << "c contains: " << c << endl;

return 0;
}


Output
a contains: 39
b contains: 40.112
c contains: 4855.82





6. 

C + + дазваляе аб'яўляць зменную лакальнай для цыкла:


using namespace std;
#include <iostream>

int main ()
{
int i; // Simple declaration of i
i = 487;

for (int i = 0; i < 4; i++) // Local declaration of i
{
cout << i << endl; // This outputs 0, 1, 2 and 3
}

cout << i << endl; // This outputs 487

return 0;
}


Output
0
1
2
3
487



У выпадку, калі пераменная не абвешчаная недзе вышэй завесы, вы можаце захацець выкарыстаць яго пад пятлю. Некаторыя раннія C + + кампілятараў пагадзіцца з гэтым. Тады пераменная значэнне, якое было, калі пятля складу. Вы не павінны рабіць гэтага. Гэта лічыцца дрэннай практыкай:


using namespace std;
#include <iostream>

int main ()
{

for (int i = 0; i < 4; i++)
{
cout << i << endl;
}

cout << i << endl; // Bad practice!
i += 5; // Bad practice!
cout << i << endl; // Bad practice!

return 0;
}


Gnu C + + кампілятар скардзіцца
t.cpp: У "функцыя" Int п ():
t.cpp: 12: памылка: пошук імя "Я" змяніў новых ISO "за" агляднага
t.cpp: 7: памылка: выкарыстанне састарэлых абавязковых на "Я"





7.

Глабальная пераменная можа быць, нават калі для іншай зменнай з такой жа назвай быў абвешчаны ўнутры функцыі:


using namespace std;
#include <iostream>

double a = 128;

int main ()
{
double a = 256;

cout << "Local a: " << a << endl;
cout << "Global a: " << ::a << endl;

return 0;
}


Output
Local a:  256
Global a: 128





8. 

Гэта можна зрабіць адной зменнай быць іншай:


using namespace std;
#include <iostream>

int main ()
{
double a = 3.1415927;

double &b = a; // b is a

b = 89;

cout << "a contains: " << a << endl; // Displays 89.

return 0;
}

Output
a contains: 89



(Калі вы прывыклі да паказальнікаў і абсалютна хочаце ведаць, што адбываецца, проста думаю, двайны & B = перакладаецца ў два разы * B = & і ўсе наступныя б замененыя * б.)

Значэнне B спасылка не можа быць зменена пасля яе аб'явы. Напрыклад, вы не можаце напісаць, некалькі радкоў далей, і B = C, чакаючы, што б у цяперашні час з. Ён не будзе працаваць. Усё гэта кажа аб дэкларацыі лініі б. Даведнік б і зменныя ў шлюбе на гэтую лінію і нічога не будзе падзяляць іх.

Спіс літаратуры можа быць выкарыстана, каб змяніць функцыі выкліку зменнай:


using namespace std;
#include <iostream>

void change (double &r, double s)
{
r = 100;
s = 200;
}

int main ()
{
double k, m;

k = 3;
m = 4;

change (k, m);

cout << k << ", " << m << endl; // Displays 100, 4.

return 0;
}


Output
100, 4



Калі вы прывыклі да паказальнікаў ў C і незразумела, як менавіта праграма ўказаных работ, вось як C + + кампілятар будзе перавесці яго на C:


using namespace std;
#include <iostream>

void change (double *r, double s)
{
*r = 100;
s = 200;
}

int main ()
{
double k, m;

k = 3;
m = 4;

change (&k, m);

cout << k << ", " << m << endl; // Displays 100, 4.

return 0;
}


Output
100, 4



Спасылкі могуць быць выкарыстаны, каб функцыя вяртання зменнай:


using namespace std;
#include <iostream>

double &biggest (double &r, double &s)
{
if (r > s) return r;
else return s;
}

int main ()
{
double k = 3;
double m = 7;

cout << "k: " << k << endl; // Displays 3
cout << "m: " << m << endl; // Displays 7
cout << endl;

biggest (k, m) = 10;

cout << "k: " << k << endl; // Displays 3
cout << "m: " << m << endl; // Displays 10
cout << endl;

biggest (k, m) ++;

cout << "k: " << k << endl; // Displays 3
cout << "m: " << m << endl; // Displays 11
cout << endl;

return 0;
}


Output
k: 3
m: 7

k: 3
m: 10

k: 3
m: 11




Ізноў жа, калі вы прывыклі да арыфметыку паказальнікаў, і калі вам цікава, як праграмы названых работ, уявіце сабе, што кампілятар перавёў яго на наступнай стандартнай праграмы C:


using namespace std;
#include <iostream>

double *biggest (double *r, double *s)
{
if (*r > *s) return r;
else return s;
}

int main ()
{
double k = 3;
double m = 7;

cout << "k: " << k << endl;
cout << "m: " << m << endl;
cout << endl;

(*(biggest (&k, &m))) = 10;

cout << "k: " << k << endl;
cout << "m: " << m << endl;
cout << endl;

(*(biggest (&k, &m))) ++;

cout << "k: " << k << endl;
cout << "m: " << m << endl;
cout << endl;

return 0;
}


Output
k: 3
m: 7

k: 3
m: 10

k: 3
m: 11




І, у канцы, для людзей, якія павінны мець справу з паказальнікамі яшчэ не падабаецца, спасылкі карысныя для эфектыўнага ААН-паказальнік пераменных. Сцеражыцеся гэта лічыцца дрэннай практыкай. Вы можаце патрапіць у бяду. Гл., напрыклад, http://www.embedded.com/story/OEG20010311S0024.


using namespace std;
#include <iostream>

double *silly_function () // This function returns a pointer to a double
{
static double r = 342;
return &r;
}

int main ()
{
double *a;

a = silly_function();

double &b = *a; // Now b is the double towards which a points!

b += 1; // Great!
b = b * b; // No need to write *a everywhere!
b += 4;

cout << "Content of *a, b and r: " << b << endl;

return 0;
}


Output
Content of *a, b and r: 117653





9. 

Прасторы імёнаў могуць быць абвешчаныя. Зменныя, абвешчаныя ўнутры прасторы імёнаў можна выкарыстоўваць дзякуючы:: Аператар:


using namespace std;
#include <iostream>
#include <cmath>

namespace first
{
int a;
int b;
}

namespace second
{
double a;
double b;
}

int main ()
{
first::a = 2;
first::b = 5;

second::a = 6.453;
second::b = 4.1e4;

cout << first::a + second::a << endl;
cout << first::b + second::b << endl;

return 0;
}


Output
8.453
41005





10. 

Калі функцыя ня ўтрымлівае простыя радкі кода, не выкарыстоўвае для завес і да т.п., ён можа быць абвешчаны ў лінію. Гэта значыць, яе код будзе ўстаўлена ўсюды выкарыстоўваецца функцыя. Гэта нешта накшталт макрас. Асноўным перавагай з'яўляецца праграма будзе хутчэй. Невялікі недахоп гэта будзе больш, таму што поўны код функцыі было ўстаўлена ўсюды, дзе ён выкарыстоўваецца:


using namespace std;
#include <iostream>
#include <cmath>

inline double hypothenuse (double a, double b)
{
return sqrt (a * a + b * b);
}

int main ()
{
double k = 6, m = 9;

// Next two lines produce exactly the same code:

cout << hypothenuse (k, m) << endl;
cout << sqrt (k * k + m * m) << endl;

return 0;
}


Output
10.8167
10.8167



(Убудаваныя функцыі былі дададзены ў C99 C і ANSI C 2000 года).





11.

Вы ведаеце, класічнай структуры кіравання C: Сапраўды, калі, рабіць, у той час, перамыкач... C + + дадае яшчэ адну структуру кіравання імя Выключэнне:


using namespace std;
#include <iostream>
#include <cmath>

int main ()
{
int a, b;

cout << "Type a number: ";
cin >> a;
cout << endl;

try
{
if (a > 100) throw 100;
if (a < 10) throw 10;
throw a/3;
}
catch (int result)
{
cout << "Result is: " << result << endl;
b = result + 1;
}

cout << "b contains: " << b << endl;

cout << endl;

// another example of exception use:

char zero [] = "zero";
char pair [] = "pair";
char notprime [] = "not prime";
char prime [] = "prime";

try
{
if (a == 0) throw zero;
if ((a/2) * 2 == a) throw pair;
for (int i = 3; i <= sqrt (a); i++)
{
if ((a/i) * i == a) throw notprime;
}
throw prime;
}
catch (char *conclusion)
{
cout << "The number you typed is "<< conclusion << endl;
}

cout << endl;

return 0;
}


Выхад
Калі ласка, увядзіце лік: 5

Вынік: 10
B змяшчае: 11

Нумар, які набраў простае






12. 

Гэта можна вызначыць параметры па змаўчанні для функцыі:


using namespace std;
#include <iostream>

double test (double a, double b = 7)
{
return a - b;
}

int main ()
{
cout << test (14, 5) << endl; // Displays 14 - 5
cout << test (14) << endl; // Displays 14 - 7

return 0;
}


Output
9
7





13. 

Адным з важных пераваг мовы З + + з'яўляецца функцыя перагрузкі. Розныя функцыі могуць мець тое ж імя, названае што-та дазваляе кампілятару адрозненне паміж імі: колькасць параметраў, тып параметраў...


using namespace std;
#include <iostream>

double test (double a, double b)
{
return a + b;
}

int test (int a, int b)
{
return a - b;
}

int main ()
{
double m = 7, n = 4;
int k = 5, p = 3;

cout << test(m, n) << ", " << test(k, p) << endl;

return 0;
}


Output
11, 2





14. 

Перагрузка аператараў можа быць выкарыстаны для перагледзець асноўныя сімвалічныя аператараў для новых відаў параметраў:


using namespace std;
#include <iostream>

struct vector
{
double x;
double y;
};

vector operator * (double a, vector b)
{
vector r;

r.x = a * b.x;
r.y = a * b.y;

return r;
}

int main ()
{
vector k, m; // No need to type "struct vector"

k.x = 2; // To be able to write
k.y = -1; // k = vector (2, -1)
// see chapter 19.

m = 3.1415927 * k; // Magic!

cout << "(" << m.x << ", " << m.y << ")" << endl;

return 0;
}


Output
(6.28319, -3.14159)



Акрамя множання, 43 іншых асноўных C + + аператары могуць быць перагружаны, у тым ліку + =, + +, масіў [], і гэтак далей...

Аператара <<, звычайна выкарыстоўваецца для бінарных перамяшчэнне цэлых лікаў, могуць быць перагружаны, каб даць выхад на паток, а не (напрыклад, суд <<). Можна перагрузкі аператара <<ў далейшым для вытворчасці новых тыпаў дадзеных, як і вектары:


using namespace std;
#include <iostream>

struct vector
{
double x;
double y;
};

ostream& operator << (ostream& o, vector a)
{
o << "(" << a.x << ", " << a.y << ")";
return o;
}

int main ()
{
vector a;

a.x = 35;
a.y = 23;

cout << a << endl; // Displays (35, 23)

return 0;
}


Output
(35, 23)





15. 

Стаміліся ад вызначаюць тую ж функцыю ў пяць разоў? Адно з азначэнняў для Int параметраў тыпу, адно азначэнне для падвойных параметраў тыпу, адно азначэнне для тып з плаваючай коскай параметры... Хіба вы не забывайце аднаго тыпу? Што рабіць, калі новы тып дадзеных выкарыстоўваецца? Няма праблем: C + + кампілятар можа аўтаматычна генерыраваць ўсе версіі функцыі, што трэба! Проста скажыце гэта, як функцыя выглядае, абвясціўшы пра шаблон функцыі:


using namespace std;
#include <iostream>

template <class ttype>
ttype minimum (ttype a, ttype b)
{
ttype r;

r = a;
if (b < a) r = b;

return r;
}

int main ()
{
int i1, i2, i3;
i1 = 34;
i2 = 6;
i3 = minimum (i1, i2);
cout << "Most little: " << i3 << endl;

double d1, d2, d3;
d1 = 7.9;
d2 = 32.1;
d3 = minimum (d1, d2);
cout << "Most little: " << d3 << endl;

cout << "Most little: " << minimum (d3, 3.5) << endl;

return 0;
}


Выхад
Большасць маленькіх: 6
Большасць маленькіх: 7,9
Большасць маленькіх: 3,5



Мінімуму функцыі выкарыстоўваецца тры разы вышэй праграмы, але C + + кампілятар генэруе толькі дзве версіі: Int мінімуму (Int, Int б) і двайны мінімум (двайны, падвойны бы). Гэта робіць працу за ўсю праграму.

Што адбудзецца, калі вы спрабавалі нешта накшталт разліку мінімальнага (i1, D1)? Кампілятар паведамілі, што, як памылку. Гэта таму, што шаблён сцвярджае, што абодва параметра маюць аднолькавы тып.

Вы можаце выкарыстоўваць адвольнае колькасць шаблонаў розных тыпаў дадзеных у вызначэнне шаблону. І не ўсе тыпы параметраў павінны быць шаблонаў, некаторыя з іх могуць быць стандартныя тыпы або пэўны карыстальнікам (знак, цэлае лік,...). Вось прыклад, дзе мінімальная функцыя прымае параметры любога тыпу (розных або аднолькавых) і выхады значэнне, якое мае тып першага параметру:


using namespace std;
#include <iostream>

template <class type1, class type2>
type1 minimum (type1 a, type2 b)
{
type1 r, b_converted;
r = a;
b_converted = (type1) b;
if (b_converted < a) r = b_converted;
return r;
}

int main ()
{
int i;
double d;

i = 45;
d = 7.41;

cout << "Most little: " << minimum (i, d) << endl;
cout << "Most little: " << minimum (d, i) << endl;
cout << "Most little: " << minimum ('A', i) << endl;

return 0;
}


Выхад
Большасць маленькіх: 7
Большасць маленькіх: 7,41
Большасць маленькіх: -

(Код ASCII характару "-" складае 45, а код "А" складае 65 гадоў.)





16.

Ключавыя словы новых і выдалення могуць быць выкарыстаны для выдзялення і вызвалення памяці. Яны чысцей, чым функцыі Malloc і свабодным ад стандартнага C.

новае] [і выдаліць []
выкарыстоўваюцца для масіваў.


using namespace std;
#include <iostream>
#include <cstring>

int main ()
{
double *d; // d is a variable whose purpose
// is to contain the address of a
// zone where a double is located


d = new double; // new allocates a zone of memory
// large enough to contain a double
// and returns its address.
// That address is stored in d.

*d = 45.3; // The number 45.3 is stored
// inside the memory zone
// whose address is given by d.

cout << "Type a number: ";
cin >> *d;

*d = *d + 5;

cout << "Result: " << *d << endl;

delete d; // delete deallocates the
// zone of memory whose address
// is given by pointer d.
// Now we can no more use that zone.


d = new double[15]; // allocates a zone for an array
// of 15 doubles. Note each 15
// double will be constructed.
// This is pointless here but it
// is vital when using a data type
// that needs its constructor be
// used for each instance.

d[0] = 4456;
d[1] = d[0] + 567;

cout << "Content of d[1]: " << d[1] << endl;

delete [] d; // delete [] will deallocate the
// memory zone. Note each 15
// double will be destructed.
// This is pointless here but it
// is vital when using a data type
// that needs its destructor be
// used for each instance (the ~
// method). Using delete without
// the [] would deallocate the
// memory zone without destructing
// each of the 15 instances. That
// would cause memory leakage.

int n = 30;

d = new double[n]; // new can be used to allocate an
// array of random size.
for (int i = 0; i < n; i++)
{
d[i] = i;
}

delete [] d;


char *s;

s = new char[100];

strcpy (s, "Hello!");

cout << s << endl;

delete [] s;

return 0;
}


Output
Type a number: 6
Result: 11
Content of d[1]: 5023
Hello!





17.

У стандартным C структура ня ўтрымлівае дадзеныя. У C + + вызначэнне структуры можа таксама ўключаць функцыі. Гэтыя функцыі належаць структуру і прызначаныя для працы з дадзенымі структуры. Гэтыя функцыі называюцца метадамі. У прыведзеным ніжэй прыкладзе вызначае метад паверхні () на вектар структуры:


using namespace std;
#include <iostream>

struct vector
{
double x;
double y;

double surface ()
{
double s;
s = x * y;
if (s < 0) s = -s;
return s;
}
};

int main ()
{
vector a;

a.x = 3;
a.y = 4;

cout << "The surface of a: " << a.surface() << endl;

return 0;
}


Output
The surface of a: 12



У прыведзеным вышэй прыкладзе, з'яўляецца асобнікам структуры "Вектар". (Звярніце ўвагу, што ключавое слова "структура" не было неабходнасці пры аб'яўленні вектар.)

Гэтак жа, як функцыя, метад можа быць перагрузка любога C + + аператар, мець любую колькасць параметраў (яшчэ адзін параметр заўсёды няяўна: напрыклад ён дзейнічае на), вяртаць любы тып параметра, ці вярнуць параметр не на ўсіх.


Што такое клас? Гэта структура, якая трымае свае дадзеныя ўтоеныя. Толькі метады класа могуць атрымаць доступ да дадзеных. Вы не можаце атрымаць доступ да дадзеных непасрэдна, калі гэта дапускаецца грамадскасці: дырэктывы. Вось прыклад вызначэння класа. Ён паводзіць сябе сапраўды гэтак жа, як структура прыведзеным вышэй прыкладзе, таму што клас дадзеных х і ў вызначаюцца як грамадскасць:


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;

double surface ()
{
double s;
s = x * y;
if (s < 0) s = -s;
return s;
}
};

int main ()
{
vector a;

a.x = 3;
a.y = 4;

cout << "The surface of a: " << a.surface() << endl;

return 0;
}


Выхад
Паверхня: 12



У прыведзеным вышэй прыкладзе, п () функцыю змянення даных асобніка напрамую, з дапамогай сякеры = 3 і ау = 4. Гэта стала магчымым дзякуючы грамадскасці: дырэктывы ў вызначэнні класа. Гэта лічыцца дрэннай практыкай. Гл. главу 30.


Метад можа змяняць зменныя асобніка ён дзейнічае на:


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;

vector its_oposite()
{
vector r;

r.x = -x;
r.y = -y;

return r;
}

void be_oposited()
{
x = -x;
y = -y;
}

void be_calculated (double a, double b, double c, double d)
{
x = a - c;
y = b - d;
}

vector operator * (double a)
{
vector r;

r.x = x * a;
r.y = y * a;

return r;
}
};

int main ()
{
vector a, b;

a.x = 3;
a.y = 5;

b = a.its_oposite();

cout << "Vector a: " << a.x << ", " << a.y << endl;
cout << "Vector b: " << b.x << ", " << b.y << endl;

b.be_oposited();
cout << "Vector b: " << b.x << ", " << b.y << endl;

a.be_calculated (7, 8, 3, 2);
cout << "Vector a: " << a.x << ", " << a.y << endl;

a = b * 2;
cout << "Vector a: " << a.x << ", " << a.y << endl;

a = b.its_oposite() * 2;
cout << "Vector a: " << a.x << ", " << a.y << endl;

cout << "x of oposite of a: " << a.its_oposite().x << endl;

return 0;
}


Выхад
Вектарны: 3, 5
Вектарны B: -3, -5
Вектарны B: 3, 5
Вектарны: 4, 6
Вектарны: 6, 10
Вектарны: -6, -10
х супрацьлеглае: 6





18.

Вельмі спецыяльныя і асноўныя метады канструктар і деструктор. Яны аўтаматычна выклікаецца кожны раз, калі экземпляр класа ствараецца або знішчаныя (абвестка зменнай, у канцы праграмы, новыя, выдаляць...).

Канструктар ініцыялізуе зменныя, напрыклад, зрабіць некаторыя разлікі, вылучыць некаторы колькасць памяці для, напрыклад, выхад якой-то тэкст... усё, што неабходна.

Вось прыклад вызначэння класа з двума перагружаных канструктараў:


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;

vector () // same name as class
{
x = 0;
y = 0;
}

vector (double a, double b)
{
x = a;
y = b;
}

};

int main ()
{
vector k; // vector () is called

cout << "vector k: " << k.x << ", " << k.y << endl << endl;

vector m (45, 2); // vector (double, double) is called

cout << "vector m: " << m.x << ", " << m.y << endl << endl;

k = vector (23, 2); // vector created, copied to k, then erased

cout << "vector k: " << k.x << ", " << k.y << endl << endl;

return 0;
}


Выхад
вектар K: 0, 0

вектар м: 45, 2

вектара да: 23, 2




Гэта добрая практыка, каб старацца не перагружаць канструктары. Лепш за ўсё, каб абвясціць толькі адзін канструктар, і даць яму па змаўчанні параметры магчымасці:


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}
};

int main ()
{
vector k;
cout << "vector k: " << k.x << ", " << k.y << endl << endl;

vector m (45, 2);
cout << "vector m: " << m.x << ", " << m.y << endl << endl;

vector p (3);
cout << "vector p: " << p.x << ", " << p.y << endl << endl;

return 0;
}


Выхад
вектар K: 0, 0

вектар м: 45, 2

вектара р: 3, 0




Деструктор часта непатрэбныя. Вы можаце выкарыстоўваць яго, каб зрабіць некаторыя разлікі, калі асобнік будзе знішчаны або выхад нейкі тэкст для адладкі... Але калі зменныя асобніка паказаць на некаторыя выдзеленай памяці, то роля деструктора з'яўляецца істотным: ён павінен вызваліць гэтую памяць! Вось прыклад такога прыкладання:


using namespace std;
#include <iostream>
#include <cstring>

class person
{
public:

char *name;
int age;

person (char *n = "no name", int a = 0)
{
name = new char [100]; // better than malloc!
strcpy (name, n);
age = a;
cout << "Instance initialized, 100 bytes allocated" << endl;
}

~person () // The destructor
{
delete name; // instead of free!

// delete [] name would be more
// academic but it is not vital
// here since the array contains
// no C++ sub-objects that need
// to be deleted.

cout << "Instance going to be deleted, 100 bytes freed" << endl;
}
};

int main ()
{
cout << "Hello!" << endl << endl;

person a;
cout << a.name << ", age " << a.age << endl << endl;

person b ("John");
cout << b.name << ", age " << b.age << endl << endl;

b.age = 21;
cout << b.name << ", age " << b.age << endl << endl;

person c ("Miki", 45);
cout << c.name << ", age " << c.age << endl << endl;

cout << "Bye!" << endl << endl;

return 0;
}


Выхад
Прывітанне!

Асобнік ініцыялізаваны, 100 байтаў, вылучаных
не імя, узрост 0

Асобнік ініцыялізаваны, 100 байтаў, вылучаных
Джон, ва ўзросце ад 0

Джон, 1921 г.

Асобнік ініцыялізаваны, 100 байтаў, вылучаных
Мікі, 45 гадоў

Пакуль!

Асобнік будзе выдалены, 100 байт вызваленыя
Асобнік будзе выдалены, 100 байт вызваленыя
Асобнік будзе выдалены, 100 байт вызваленыя



Вось кароткі прыклад вызначэння класа масіва. Метад, перагрузка аператара [] і выхады спасылкі (&) выкарыстоўваецца для таго, каб генерыраваць памылку, калі спроба атрымаць доступ да дадзеных за межы масіва:


using namespace std;
#include <iostream>
#include <cstdlib>

class array
{
public:
int size;
double *data;

array (int s)
{
size = s;
data = new double [s];
}

~array ()
{
delete [] data;
}

double &operator [] (int i)
{
if (i < 0 || i >= size)
{
cerr << endl << "Out of bounds" << endl;
exit (EXIT_FAILURE);
}
else return data [i];
}
};

int main ()
{
array t (5);

t[0] = 45; // OK
t[4] = t[0] + 6; // OK
cout << t[4] << endl; // OK

t[10] = 7; // error!

return 0;
}


Выхад
51

За межы





19.

Калі вы разыгрывае аб'екта як вектар, усё будзе адбывацца няправільна. Напрыклад, калі вектар Да змяшчае (4, 7), пасля прывядзення M = K вектар M будзе ўтрымліваць (4, 7) таксама. Значэнні кх, ку проста былі скапіяваныя MX і мая. Зараз выкажам здагадку, што вы гуляеце з аб'ектамі, як чалавек класам вышэй. Гэтыя аб'екты ўтрымліваюць паказальнік на радок знакаў. Калі вы прыводзіце чалавека аб'екта ў пісьмовым выглядзе р = г, неабходна, каб некаторая функцыя робіць працу, каб р правільную копію г. У адваротным выпадку, p.name будзе ўказваць на той жа фізічны Літарная як r.name. Больш таго, былы Літарная, на якую паказвае p.name губляецца і становіцца зомбі памяці. Вынік будзе катастрафічных: беспарадак паказальнікі і страчаныя дадзеныя. Метады, якія будуць рабіць гэтую працу з'яўляюцца канструктар капіявання і перагрузка аператара =:


using namespace std;
#include <iostream>
#include <cstring>

class person
{
public:

char *name;
int age;

person (char *n = "no name", int a = 0)
{
name = new char[100];
strcpy (name, n);
age = a;
}

person (const person &s) // The COPY CONSTRUCTOR
{
 name = new char[100];
  strcpy (name, s.name);
 age = s.age;
 }

person& operator= (const person &s) // overload of =
{
strcpy (name, s.name);
age = s.age;
return *this;
}

~person ()
{
delete [] name;
}
};

void modify_person (person& h)
{
h.age += 7;
}

person compute_person (person h)
{
h.age += 7;
return h;
}


int main ()
{
person p;
cout << p.name << ", age " << p.age << endl << endl;
// output: no name, age 0

person k ("John", 56);
cout << k.name << ", age " << k.age << endl << endl;
// output: John, age 56

p = k;
cout << p.name << ", age " << p.age << endl << endl;
// output: John, age 56

p = person ("Bob", 10);
cout << p.name << ", age " << p.age << endl << endl;
// output: Bob, age 10

// Neither the copy constructor nor the overload
// of = are needed for this operation that modifies
// p since just the reference towards p is passed to
// the function modify_person:
modify_person (p);
cout << p.name << ", age " << p.age << endl << endl;
// output: Bob, age 17

// The copy constructor is called to pass a complete
// copy of p to the function compute_person. The
// function uses that copy to make its computations
// then a copy of that modified copy is made to
// return the result. Finaly the overload of = is
// called to paste that second copy inside k:
k = compute_person (p);
cout << p.name << ", age " << p.age << endl << endl;
// output: Bob, age 17
cout << k.name << ", age " << k.age << endl << endl;
// output: Bob, age 24

return 0;
}


Выхад
не імя, узрост 0

Джон, 56 гадоў

Джон, 56 гадоў

Боб, 10 гадоў

Боб, 17 гадоў

Боб, 17 гадоў

Боб, 24 гадоў




Канструктар капіявання дазваляе вашай праграмы, каб зрабіць копіі выпадках, калі рабіць разлікі. Гэта з'яўляецца ключавым метадам. У ходзе разлікаў, асобнікі ствараюцца для захоўвання прамежкавых вынікаў. Яны будуць зменены, літых і знішчана без вашага ведама. Менавіта таму гэтыя метады могуць быць карысныя нават для простых аб'ектаў (гл. раздзел 14.).

Ва ўсіх прыведзеных вышэй прыкладах метады вызначаны ўнутры вызначэння класа. Гэта аўтаматычна робіць іх убудаваныя метады.





20.

Калі метад не можа быць убудаваным, ці вы не хочаце, каб быць убудаваным, або калі вы хочаце вызначэнне класа змяшчае мінімальнае колькасць інфармацыі (ці вы проста хочаце звычайна асобна. Ч загалоўка файла і. СРР файл з зыходным тэкстам), то вам трэба толькі паставіць прататып метаду ўнутры класа і вызначыць метад ніжэй класа (ці ў асобным зыходным файле CPP.):


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;

double surface(); // The; and no {} show it is a prototype
};

double vector::surface()
{
double s = 0;

for (double i = 0; i < x; i++)
{
s = s + y;
}

return s;
}

int main ()
{
vector k;

k.x = 4;
k.y = 5;

cout << "Surface: " << k.surface() << endl;

return 0;
}


Output
Surface: 20



Для пачаткоўцаў:

Калі вы збіраецеся распрацоўваць сур'ёзныя C ці C + + праграму, неабходна аддзяліць зыходны код у. Ч файлы загалоўкаў і. CPP зыходных файлаў. Гэта кароткі прыклад таго, як гэта робіцца. Праграма вышэй падзелены на тры файла:

Vector.h файл загалоўка:


 клас вектар 
{
грамадскасці:

Double X;
двайны ў;

двайны паверхні ();
};



Файл vector.cpp Крыніца:


using namespace std;
#include "vector.h"

double vector::surface()
{
double s = 0;

for (double i = 0; i < x; i++)
{
s = s + y;
}

return s;
}



І яшчэ адзін файл main.cpp Крыніца:


using namespace std;
#include <iostream>
#include "vector.h"

int main ()
{
vector k;

k.x = 4;
k.y = 5;

cout << "Surface: " << k.surface() << endl;

return 0;
}


Мяркуючы, vector.cpp з'яўляецца дасканалым, вы скампіляваць яго раз і назаўсёды ст. O "аб'ектны файл". Прыведзеная ніжэй каманда вырабляе гэты код файла аб'ект, званы vector.o:

G + +-C vector.cpp

Кожны раз, калі вы змяняеце зыходны файл main.cpp, вы скампіляваць яго ў выкананы файл, скажам test20. Вы кажаце кампілятар відавочна, што ён мае на спасылку vector.o файл аб'екта ў канчатковы test20 выкананы файл:

G + + main.cpp vector.o-O test20

Запусціце выкананы файл наступным чынам:

./test20

Гэта мае шэраг пераваг:
  • Зыходны код vector.cpp неабходна быць складзены толькі адзін раз. Гэта эканоміць шмат часу на вялікіх праектах. (Узоры vector.o файл у выкананы test20 вельмі хутка.)
  • Вы можаце даць каму-то. Файла ч і. Вываду файла (ов). Такім чынам, яны могуць выкарыстоўваць праграмнае забеспячэнне, але не змяніць яго, таму што яны не маюць. CPP файла (ов) (не занадта спадзявацца на гэта, чакаць, пакуль вы майстар на гэтыя пытанні).
Звярніце ўвагу, вы можаце скампіляваць main.cpp таксама ў аб'ектны файл, а затым звязаць яго з vector.o:

G + +-C main.cpp

G + + main.o vector.o test20

Гэта блукае ад "адрозненні паміж C і C + +" тэму, але калі вы хочаце выглядаць як сапраўдны праграміст, вы павінны кандэнсавацца вышэй каманды ў Makefile і кампіляцыі з дапамогай каманды зрабіць. Змесціва файла пад гэта спрошчаны варыянт такога Makefile. Скапіруйце яго ў файл з імем Makefile. Калі ласка, звярніце ўвагу, і гэта вельмі важна, што прастора перад G + + каманд з'яўляецца абавязковым і што гэта знак табуляцыі. Не ўводзіце прабел тут. Замест гэтага выкарыстоўвайце табуляцыю ключ (поўны злева ад клавіятуры, вышэй блакіроўка вечка).


 test20: main.o vector.o 
G + + main.o vector.o-O test20

main.o: main.cpp vector.h
G + +-C main.cpp

vector.o: vector.cpp vector.h
G + +-C vector.cpp


Для таго, каб выкарыстаць гэты Makefile для кампіляцыі, тыпу:

зрабіць test20

Зрабіць каманда разабраць праз файл Makefile і высветліць, што ён павінен рабіць. Каб пачаць з таго, што сказалі, што test20 залежыць ад main.o і vector.o. Так будзе аўтаматычна запускаць "зрабіць main.o" і "зрабіць vector.o". Тады яна будзе правяраць, калі test20 ўжо існуе і правярае дату маркі test20, main.o і vector.o. Калі test20 ўжо існуе і main.o і vector.o ёсць датай раней, чым test20, каб каманда вызначае, што бягучая версія test20 да даты, таму ён не мае нічога агульнага. Яна будзе проста паведаміць пра гэта нічога не рабіў. У адваротным выпадку, калі test20 не існуе, або main.o або vector.o больш познія, чым test20, каманда, якая стварае да сучаснай версіі test20 выканана: G + + main.o vector.o-O test20.

Гэта наступная версія Makefile бліжэй да стандартнай Makefile:


 ўсё: test20 

test20: main.o vector.o
G + + main.o vector.o-O test20

main.o: main.cpp vector.h
G + +-C main.cpp

vector.o: vector.cpp vector.h
G + +-C vector.cpp

Чысціня:
RM-F *. Аб test20 * # *


Вы запуску кампіляцыі проста набраўшы каманду зрабіць. Першая радок у файле Makefile вынікае, што калі вы проста набярэце зрабіць вы хацелі "зрабіць test20":

зрабіць

Гэтая каманда выдаляе ўсе файлы, створаныя ў працэсе кампіляцыі і ўсе тэкставыя файлы рэзервовых дзід рэдактара:

зрабіць чыстай






21.

Калі метад прымяняецца да прыкладу, што метад можа выкарыстаць зменныя асобніка, змяняць іх... Але часам гэта неабходна ведаць адрас экзэмпляра. Няма праблем, ключавое слова гэта прызначана для гэтай мэты:


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

double module()
{
return sqrt (x * x + y * y);
}

void set_length (double a = 1)
{
double length;

length = this->module();

x = x/length * a;
y = y/length * a;
}
};

int main ()
{
vector c (3, 5);

cout << "The module of vector c: " << c.module() << endl;

c.set_length(2); // Transforms c in a vector of size 2.

cout << "The module of vector c: " << c.module() << endl;

c.set_length(); // Transforms b in an unitary vector.

cout << "The module of vector c: " << c.module() << endl;

return 0;
}


Выхад
Модуль вектара C: 5,83095
Модуль вектара з: 2
Модуль вектара З: 1





22.

Вядома, можна абвясціць масіў аб'ектаў:


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

double module ()
{
return sqrt (x * x + y * y);
}
};

int main ()
{
vector s [1000];

vector t[3] = {vector(4, 5), vector(5, 5), vector(2, 4)};

s[23] = t[2];

cout << t[0].module() << endl;

return 0;
}


Выхад
6.40312





23.

Вось прыклад поўнага вызначэння класа:


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double = 0, double = 0);

vector operator + (vector);
vector operator - (vector);
vector operator - ();
vector operator * (double a);
double module();
void set_length (double = 1);
};

vector::vector (double a, double b)
{
x = a;
y = b;
}

vector vector::operator + (vector a)
{
return vector (x + a.x, y + a.y);
}

vector vector::operator - (vector a)
{
return vector (x - a.x, y - a.y);
}

vector vector::operator - ()
{
return vector (-x, -y);
}

vector vector::operator * (double a)
{
return vector (x * a, y * a);
}

double vector::module()
{
return sqrt (x * x + y * y);
}

void vector::set_length (double a)
{
double length = this->module();

x = x/length * a;
y = y/length * a;
}

ostream& operator << (ostream& o, vector a)
{
o << "(" << a.x << ", " << a.y << ")";
return o;
}

int main ()
{
vector a;
vector b;
vector c (3, 5);

a = c * 3;
a = b + c;
c = b - c + a + (b - a) * 7;
c = -c;

cout << "The module of vector c: " << c.module() << endl;

cout << "The content of vector a: " << a << endl;
cout << "The oposite of vector a: " << -a << endl;

c.set_length(2); // Transforms c in a vector of size 2.

a = vector (56, -3);
b = vector (7, c.y);

b.set_length(); // Transforms b in an unitary vector.

cout << "The content of vector b: " << b << endl;

double k;
k = vector(1, 1).module(); // k will contain 1.4142.
cout << "k contains: " << k << endl;

return 0;
}


Выхад
Модуль вектара C: 40,8167
Змест вектар: (3, 5)
Процілегла вектару: (-3, -5)
Змест вектар B: (0.971275, 0,23796)
Да змяшчае: 1,41421



Акрамя таго, можна вызначыць функцыі вырабляе сумы двух вектараў, не згадваючы яго ў вызначэнні класа вектар. Тады ён не будзе метад класа вектар, а хутчэй проста функцыя, якая выкарыстоўвае вектары:


 вектарны аператар + (вектар, вектар B) 
{
вяртанне вектара (ах + BX, ау + BY);
}



У прыклад поўнага вызначэння класа, вышэй, множанне вектара на двайны вызначана. Дапусцім, мы хочам множання падвойных вектарам, якія будуць вызначаны занадта. Тады мы павінны напісаць ізаляваных функцыя па-за класа:


 вектарны аператар * (двайны, вектар B) 
{
вяртанне вектар (* BX, * па);
}



Вядома, ключавыя словы і выдаляць новыя працы для асобнікаў класаў таксама. Больш таго, новыя аўтаматычна выклікае канструктар для ініцыялізацыі аб'ектаў, і выдаліць аўтаматычна выклікае деструктор да вызваляючы памяць асобнік зменныя прымаюць:


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double = 0, double = 0);

vector operator + (vector);
vector operator - (vector);
vector operator - ();
vector operator * (double);
double module();
void set_length (double = 1);
};

vector::vector (double a, double b)
{
x = a;
y = b;
}

vector vector::operator + (vector a)
{
return vector (x + a.x, y + a.y);
}

vector vector::operator - (vector a)
{
return vector (x - a.x, y - a.y);
}

vector vector::operator - ()
{
return vector (-x, -y);

}

vector vector::operator * (double a)
{
return vector (a * x, a * y);
}

double vector::module()
{
return sqrt (x * x + y * y);
}

void vector::set_length (double a)
{
vector &the_vector = *this;

double length = the_vector.module();

x = x/length * a;
y = y/length * a;
}

ostream& operator << (ostream& o, vector a)
{
o << "(" << a.x << ", " << a.y << ")";
return o;
}

int main ()
{
vector c (3, 5);

vector *r; // r is a pointer to a vector.

r = new vector; // new allocates the memory necessary
cout << *r << endl; // to hold a vectors' variable,
// calls the constructor who will
// initialize it to 0, 0. Then finally
// new returns the address of the vector.

r->x = 94;
r->y = 345;
cout << *r << endl;

*r = vector (94, 343);
cout << *r << endl;

*r = *r - c;
r->set_length(3);
cout << *r << endl;

*r = (-c * 3 + -*r * 4) * 5;
cout << *r << endl;

delete r; // Calls the vector destructor then
// frees the memory.

r = &c; // r points towards vector c
cout << *r << endl;

r = new vector (78, 345); // Creates a new vector.
cout << *r << endl; // The constructor will initialise
// the vector's x and y at 78 and 345

cout << "x component of r: " << r->x << endl;
cout << "x component of r: " << (*r).x << endl;

delete r;

r = new vector[4]; // creates an array of 4 vectors

r[3] = vector (4, 5);
cout << r[3].module() << endl;

delete [] r; // deletes the array

int n = 5;
r = new vector[n]; // Cute!

r[1] = vector (432, 3);
cout << r[1] << endl;

delete [] r;

return 0;
}


Output
(0, 0)
(94, 345)
(94, 343)
(0.77992, 2.89685)
(-60.5984, -132.937)
(3, 5)
(78, 345)
x component of r: 78
x component of r: 78
6.40312
(432, 3)





24. 

Адзін або некалькі зменных у класе могуць быць аб'яўлены статычнымі. У гэтым выпадку, толькі адзін асобнік гэтых пераменных існуюць, агульнымі для ўсіх экзэмпляраў класа. Ён павінен быць уведзены ў строй па-за аб'явы класа:


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;
static int count;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
count++;
}

~vector()
{
count--;
}
};

int vector::count = 0;

int main ()
{
cout << "Number of vectors:" << endl;

vector a;
cout << vector::count << endl;

vector b;
cout << vector::count << endl;

vector *r, *u;

r = new vector;
cout << vector::count << endl;

u = new vector;
cout << a.count << endl;

delete r;
cout << vector::count << endl;

delete u;
cout << b.count << endl;

return 0;
}


Output
1
2
3
4
3
2





25. 

Зменнай класа могуць таксама быць канстантныя мурашкі. Вось толькі як статычныя, за выключэннем таго, дадзена значэнне ўнутры аб'явы класа, і што значэнне не можа быць зменены:


using namespace std;
#include <iostream>

class vector
{
public:

double x;
double y;
const static double pi = 3.1415927;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

double cilinder_volume ()
{
return x * x/4 * pi * y;
}
};

int main()
{
cout << "The value of pi: " << vector::pi << endl << endl;

vector k (3, 4);

cout << "Result: " << k.cilinder_volume() << endl;

return 0;
}


Выхад
Значэнне ліку "пі": 3,14159

Вынік: 28,2743





26.

Клас можа быць вытворным ад іншага класа. Новы клас спадчыну зменныя і метады базавага класа. Дадатковыя зменныя і/або метады могуць быць дададзены:


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

double module()
{
return sqrt (x*x + y*y);
}

double surface()
{
return x * y;
}
};

class trivector: public vector // trivector is derived from vector
{
public:
double z; // added to x and y from vector

trivector (double m=0, double n=0, double p=0): vector (m, n)
{
z = p; // Vector constructor will
} // be called before trivector
// constructor, with parameters
// m and n.

trivector (vector a) // What to do if a vector is
{ // cast to a trivector
x = a.x;
y = a.y;
z = 0;
}

double module () // define module() for trivector
{
return sqrt (x*x + y*y + z*z);
}

double volume ()
{
return this->surface() * z; // or x * y * z
}
};

int main ()
{
vector a (4, 5);
trivector b (1, 2, 3);

cout << "a (4, 5) b (1, 2, 3) *r = b" << endl << endl;

cout << "Surface of a: " << a.surface() << endl;
cout << "Volume of b: " << b.volume() << endl;
cout << "Surface of base of b: " << b.surface() << endl;

cout << "Module of a: " << a.module() << endl;
cout << "Module of b: " << b.module() << endl;
cout << "Module of base of b: " << b.vector::module() << endl;

trivector k;
k = a; // thanks to trivector(vector) definition
// copy of x and y, k.z = 0
vector j;
j = b; // copy of x and y. b.z leaved out

vector *r;
r = &b;

cout << "Surface of r: " << r->surface() << endl;
cout << "Module of r: " << r->module() << endl;

return 0;
}


Выхад
(4, 5) B (1, 2, 3) * R = B

Паверхня: 20
Аб'ём B: 6
Паверхня базе B: 2
Модуль: 6,40312
Модуль B: 3,74166
Модуль базы B: 2,23607
Паверхня г: 2
Модуль R: 2,23607





27.

У праграме вышэй, г-> модуль () вылічае вектар модуля, выкарыстоўваючы X і Y, так як г быў абвешчаны вектар паказальнік. Той факт, што спадар фактычна паказвае на тривектором не прымаецца пад увагу. Калі вы хочаце праграму для праверкі тыпу завостраны прадмет і выбраць адпаведны метад, то вы павінны заявіць, што метад як віртуальны ўнутры базавага класа.

(Калі хаця б адзін з метадаў базавага класа з'яўляецца віртуальным, то "загаловак" з 4 байт дадаецца да кожнага экзэмпляры класа. Гэта дазваляе праграме вызначыць, які вектар на самай справе паказвае.) (4 байта, верагодна, ад канкрэтнай рэалізацыі. На 64-бітнай машыне можа быць, гэта 8 байт...)


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

virtual double module()
{
return sqrt (x*x + y*y);
}
};

class trivector: public vector
{
public:
double z;

trivector (double m = 0, double n = 0, double p = 0)
{
x = m; // Just for the game,
y = n; // here I do not call the vector
z = p; // constructor and I make the
} // trivector constructor do the
// whole job. Same result.

double module ()
{
return sqrt (x*x + y*y + z*z);
}
};

void test (vector &k)
{
cout << "Test result: " << k.module() << endl;
}

int main ()
{
vector a (4, 5);
trivector b (1, 2, 3);

cout << "a (4, 5) b (1, 2, 3)" << endl << endl;

vector *r;

r = &a;
cout << "module of vector a: " << r->module() << endl;

r = &b;
cout << "module of trivector b: " << r->module() << endl;

test (a);

test (b);

vector &s = b;

cout << "module of trivector b: " << s.module() << endl;

return 0;
}


Выхад
(4, 5) B (1, 2, 3)

модуль вектара: 6,40312
Модуль тривектором B: 3,74166
Вынік тэсту: 6,40312
Вынік тэсту: 3,74166
Модуль тривектором B: 3,74166





28.

Можа быць, вам цікава, калі клас можа быць атрымана з больш чым аднаго базавага класа. Адказ "так":


using namespace std;
#include <iostream>
#include <cmath>

class vector
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

double surface()
{
return fabs (x * y);
}
};

class number
{
public:

double z;

number (double a)
{
z = a;
}

int is_negative ()
{
if (z < 0) return 1;
else return 0;
}
};

class trivector: public vector, public number
{
public:

trivector(double a=0, double b=0, double c=0): vector(a,b), number(c)
{
} // The trivector constructor calls the vector
// constructor, then the number constructor,
// and in this example does nothing more.

double volume()
{
return fabs (x * y * z);
}
};

int main ()
{
trivector a(2, 3, -4);

cout << a.volume() << endl;
cout << a.surface() << endl;
cout << a.is_negative() << endl;

return 0;
}


Выхад
24
6
1





29.

Клас выснову дазваляе ствараць больш складаныя класы пабудаваныя з базавых класаў. Існуе яшчэ адно прымяненне класа выснову: дазваляе праграмісту напісаць агульныя функцыі.

Выкажам здагадку, што вы вызначыць базавы клас без якіх-небудзь пераменных. Гэта не мае ніякага сэнсу выкарыстоўваць асобнікі гэтага класа ў вашай праграме. Але тады вы пішаце функцыю, мэта якой складаецца ў сартаванні асобнікі гэтага класа. Гэтая функцыя будзе магчымасці сартаваць любы тып аб'екта, калі яна належыць клас, вытворны ад базавага класа! Адзіным умовай з'яўляецца тое, што ўнутры кожнага вызначэння вытворнага класа, усе метады, якія патрэбаў роду функцыі карэктна вызначаны:


using namespace std;
#include <iostream>
#include <cmath>

class octopus
{
public:

virtual double module() = 0; // = 0 implies function is not
// defined. This makes instances
// of this class cannot be declared.
};

double biggest_module (octopus &a, octopus &b, octopus &c)
{
double r = a.module();
if (b.module() > r) r = b.module();
if (c.module() > r) r = c.module();
return r;
}

class vector: public octopus
{
public:

double x;
double y;

vector (double a = 0, double b = 0)
{
x = a;
y = b;
}

double module()
{
return sqrt (x * x + y * y);
}
};

class number: public octopus
{
public:

double n;

number (double a = 0)
{
n = a;
}

double module()
{
if (n >= 0) return n;
else return -n;
}
};

int main ()
{
vector k (1,2), m (6,7), n (100, 0);
number p (5), q (-3), r (-150);

cout << biggest_module (k, m, n) << endl;
cout << biggest_module (p, q, r) << endl;

cout << biggest_module (p, q, n) << endl;

return 0;
}


Выхад
100
150
100



Можа быць, вы думаеце: "Добра, што гэта добрая ідэя для атрымання класаў з класа васьмінога, таму што як я магу прымяніць яго да асобнікам класа метады маёй і функцыі, якія былі распрацаваны ў агульным выглядзе для васьмінога класа. Але што, калі ёсць іншы базы клас, названы каракатицы, якая мае вельмі цікавыя метады і функцыі таксама? Павінен Ці я зрабіць свой ??выбар паміж васьміногаў і каракатиц, калі я хачу атрымаць клас? " Не, вядома, няма. Вытворнага класа могуць быць выведзеныя з абодвух васьміногаў і каракатиц. Гэта палімарфізм. Вытворны клас проста вызначыць метады, неабходныя для васьмінога разам з метадамі, неабходнымі для каракатицы:


class octopus
{
virtual double module() = 0;
};

class cuttlefish
{
virtual int test() = 0;
};

class vector: public octopus, public cuttlefish
{
double x;
double y;

double module ()
{
return sqrt (x * x + y * y);
}

int test ()
{
if (x > y) return 1;
else return 0;
}
}





30.

Грамадскасці: дырэктыва сродкаў зменных або метадаў ніжэй, могуць быць даступныя і выкарыстоўваюцца паўсюдна ў праграме.

Калі вы хочаце зменных і метадаў, якія будуць даступныя толькі для метадаў класа і метадаў вытворных класаў, то вы павінны змясціць ключавое слова абаронены: перад імі.

Калі вы хочаце зменных або метадаў, якія будуць даступныя толькі для метадаў класа, то вы павінны змясціць ключавое слова прыватнага: перад імі.

Той факт, што зменныя або метады абвешчаныя закрытым або абароненым азначае, што нічога не знешнімі па адносінах да класа можаце атрымаць доступ або выкарыстоўваць іх. Гэта інкапсуляцыі. (Калі вы хочаце, каб даць пэўныя функцыі права на доступ да гэтых пераменным і метадам, то вы павінны ўключыць функцыі прататыпа, што ўсярэдзіне вызначэння класа, якому папярэднічае ключавое слова сябар.)

Эфектыўная практыка заключаецца ў інкапсуляцыі ўсіх зменных класа. Гэта можа гучаць дзіўна, калі вы прывыклі да структурам у С. У самай справе структуру мае сэнс толькі калі вы можаце атрымаць доступ да яго даных... У C + + вы павінны стварыць метады Acces дадзеных усярэдзіне класа. У прыведзеным ніжэй прыкладзе выкарыстоўваецца просты прыклад з главы 17, але заяўляе, дадзеныя класа на абарону:


using namespace std;
#include <iostream>

class vector
{
protected:

double x;
double y;

public:

void set_x (int n)
{
x = n;
}
   void set_y (int n)
{
y = n;
}
double surface ()
{
double s;
s = x * y;
if (s < 0) s = -s;
return s;
}
};

int main ()
{
vector a;

a.set_x (3);
a.set_y (4);

cout << "The surface of a: " << a.surface() << endl;

return 0;
}


Выхад
Паверхня: 12



Прыведзены вышэй прыклад з'яўляецца крыху дзіўным, паколькі клас дадзеных X і Y могуць быць устаноўлены, але яны не могуць быць лічаны назад. Любыя спробы ў функцыі п () наступным чынам сякеру або ау прывядзе да памылкі кампіляцыі. У наступным прыкладзе, X і Y могуць быць лічаны зваротна:


using namespace std;
#include <iostream>

class vector
{
protected:

double x;
double y;

public:

void set_x (int n)
{
x = n;
}
   void set_y (int n)
{
y = n;
}

double get_x ()
{
return x;
}
   double get_y ()
{
return y;
}
double surface ()
{
double s;
s = x * y;
if (s < 0) s = -s;
return s;
}
};

int main ()
{
vector a;

a.set_x (3);
a.set_y (4);

cout << "The surface of a: " << a.surface() << endl;
cout << "The width of a: " << a.get_x() << endl;
cout << "The height of a: " << a.get_y() << endl;

return 0;
}


Выхад
Паверхня: 12
Шырыня: 3
Вышыня: 4



У C + + адзін не павінен атрымаць доступ да дадзеных з класа напрамую. Метады павінны быць абвешчаныя. Чаму гэта адбываецца? Многія прычыны існуюць. Па-першае, гэта дазваляе змяняць спосаб прадстаўлення дадзеных у межах класа. Яшчэ адна прычына, гэта дазваляе абменьвацца дадзенымі ўнутры класа крос-залежнымі. Няхай X і Y павінны заўсёды мець той жа знак, у адваротным выпадку выродлівыя рэчы могуць здарыцца... Калі хто-то дазволены доступ да дадзеных непасрэдна класа, што будзе лёгка навязаць сказаць станоўчых х і адмоўныя ў У прыкладзе, прыведзеным ніжэй, гэта строга кантралюецца:


using namespace std;
#include <iostream>

int sign (double n)
{
if (n >= 0) return 1;
return -1;
}

class vector
{
protected:

double x;
double y;

public:

void set_x (int n)
{
x = n;
if (sign (x) != sign(y)) y = -y;
}
   void set_y (int n)
{
y = n;
if (sign (y) != sign(x)) x = -x;
}

double get_x ()
{
return x;
}
   double get_y ()
{
return y;
}
double surface ()
{
double s;
s = x * y;
if (s < 0) s = -s;
return s;
}
};

int main ()
{
vector a;

a.set_x (-3);
a.set_y (4);

cout << "The surface of a: " << a.surface() << endl;
cout << "The width of a: " << a.get_x() << endl;
cout << "The height of a: " << a.get_y() << endl;

return 0;
}


Выхад
Паверхня: 12
Шырыня: 3
Вышыня: 4





31.

Давайце пагаворым аб ўвод/выснова. У C + +, што гэта вельмі шырокая тэма.

Вось праграма, якая запісвае ў файл:


using namespace std;
#include <iostream>
#include <fstream>

int main ()
{
fstream f;

f.open("test.txt", ios::out);

f << "This is a text output to a file." << endl;

double a = 345;

f << "A number: " << a << endl;

f.close();

return 0;
}


Змест файла test.txt
Гэта тэкставы выснова ў файл.
Нумар: 345



Вось праграма, якая счытвае дадзеныя з файла:



using namespace std;
#include <iostream>
#include <fstream>

int main ()
{
fstream f;
char c;

cout << "What's inside the test.txt file" << endl;
cout << endl;

f.open("test.txt", ios::in);

while (! f.eof() )
{
f.get(c); // Or c = f.get()
cout << c;
}

f.close();

return 0;
}


Выхад
Гэта тэкставы выснова ў файл.
Нумар: 345





32.

Наогул кажучы, гэта можна рабіць на знакавыя масівы жа аперацыі з файламі. Гэта вельмі карысна для пераўтварэння дадзеных і кіравання памяццю масіваў.

Вось праграма, якая запісвае ў масіў сімвалаў:


using namespace std;
#include <iostream>
#include <strstream>
#include <cstring>
#include <cmath>

int main ()
{
char a[1024];
ostrstream b(a, 1024);

b.seekp(0); // Start from first char.
b << "2 + 2 = " << 2 + 2 << ends; // ( ends, not endl )
// ends is simply the
// null character '\0'
cout << a << endl;

double v = 2;

strcpy (a, "A sinus: ");

b.seekp(strlen (a));
b << "sin (" << v << ") = " << sin(v) << ends;

cout << a << endl;

return 0;
}


Выхад
2 + 2 = 4
Сінус: грэх (2) = 0,909297



Праграма, якая счытвае дадзеныя з знакавай радкі:


using namespace std;
#include <iostream>
#include <strstream>
#include <cstring>

int main ()
{
char a[1024];
istrstream b(a, 1024);

strcpy (a, "45.656");

double k, p;

b.seekg(0); // Start from first character.
b >> k;

k = k + 1;

cout << k << endl;

strcpy (a, "444.23 56.89");

b.seekg(0);
b >> k >> p;

cout << k << ", " << p + 1 << endl;

return 0;
}


Выхад
46.656
444,23, 57,89





33.

Гэтая праграма ажыццяўляе фарматаваны выснову двума рознымі спосабамі. Калі ласка, звярніце ўвагу шырыню () і setw () мадыфікатары толькі ў сілу ў наступны пункт выхаду ў струмень. Наступныя пункты, не будзе ўплываць.


using namespace std;
#include <iostream>
#include <iomanip>

int main ()
{
int i;

cout << "A list of numbers:" << endl;
for (i = 1; i <= 1024; i *= 2)
{
cout.width (7);
cout << i << endl;
}

cout << "A table of numbers:" << endl;
for (i = 0; i <= 4; i++)
{
cout << setw(3) << i << setw(5) << i * i * i << endl;
}

return 0;
}


Выхад
Спіс нумароў:
      1
      2
      4
      8
     16
     32
     64
    128
    256
    512
   1024
Табліца адпаведнікаў нумароў:
  0    0
  1    1
  2    8
  3   27
  4   64



Зараз у вас ёсць базавыя веды аб C + +. Унутры добрыя кнігі вы даведаецеся яшчэ шмат рэчаў. Сістэма кіравання файламі з'яўляецца вельмі магутным, ён мае шмат іншых магчымасцяў, чым паказана тут. Існуе таксама шмат больш, каб сказаць аб класах: шаблонныя класы, віртуальныя класы...

Для таго, каб эфектыўна працаваць з C + + вам трэба добры даведнік, як і ён вам патрэбен для C. Вам таксама спатрэбіцца інфармацыя аб тым, як C + + выкарыстоўваецца ў вашай канкрэтнай вобласці дзейнасці. Стандартаў, глабальны падыход, прыёмы, тыповыя праблемы і іх рашэння... Лепшая рэкамендацыя, вядома, кнігі, напісаныя Бьерн Строўструп сябе (я не памятаю, хто з іх я чытаў). Кніга змяшчае наступныя амаль кожныя дэталі аб C і C + + і пабудавана такім чынам, падобныя на гэты тэкст:

Ямся на C/C + + Біблія праграміста
©right; 1998 Ямся Прэс
Лас-Вегас, ЗША

Французскае выданне:
C/C + + La Бібліі дзю programmeur
Крыс Ямся, кандыдат тэхнічных навук - Ларс Klander
Францыя: Выданні Eyrolles
www.eyrolles.com
Канада: Les Выданні Рейнальд Гуле Inc.
www.goulet.ca
ISBN 2-212-09058-7


Іншыя спасылкі:

accuwww.accu.org/bookreviews/public/reviews/0hr/index.htm

CoderSource.net:  www.codersource.net/

C++ Guide:  google-styleguide.googlecode.com/svn/trunk/cppguide.xml


Аналагічныя падручнік для Ада даступная на  www.adahome.com/Ammo/cpp2ada.html

Haskell падручнік па праграміст C:  learnyouahaskell.com

Я хацеў бы падзякаваць Дыдзье Bizzarri, Тоні Ronkko, Фрэдэрык тканіна, Джэк Лам, Мортэн Педерсен Brix, Элмер Fittery, Ана Yuseepi, Уільям Л. фарба, Bahjat Ф. Qaqish, Muthukumar Veluswamy, Марка Cimarosti, Jarrod Мілер, Николаос Pothitos, Ральф Ву, Дэйв Аберкромби, Алекс Пеннингтон, Скот Марсден, Роберт Krten, Дэйв Пантер і Cihat Imamoglu за іх натхненне, парады, дапамога, дадзеныя, паведамленні пра памылку, літаратуры і ўмацаванне англійскай рэдакцыі.


Most popular articles:


  • Photo-news blog

  • Clipart Library

  • Keywords base