CODE
|
DATA
|
STACK (системен
стек, run-time stack)
|
HEAP (динамична памет)
|
** Тип указател (pointer)
* Константа или променлива от тип указател съхранява адрес от
оперативната памет на компютъра.
* Дефиниране на променлива тип указател (език C)
Синтаксис: <име
на
тип> * <име
на променлива>
Примери: Дефиниции на указателите pk,
boss и pn.
int* pk; //
без инициализация
Employee * boss; // без инициализация
int *pn =
NULL; // с инициализация
Пример 1:
- дефиниране на променлива тип указател (pn)
и инициализация на указателя:
int *pn = new int;
Пример 2:
- дефиниране на масив (с име указател arr)
в динамичната памет с помощта на операция new:
int *arr = new int[100];
Пример 3:
-дефиниране на променлива тип указател (boss), инициализация на
указателя (операция new) и задаване на начална
стойност (конструктор) на обекта на този адрес:
Employee *boss = new Employ("John", 3200);
-конструиране на обект от
тип int,
инициализация на указателя (операция new) и задаване на
начална стойност на променливата на този адрес:
int *pn = new int(10);
STACK
|
Динамична памет адрес XXX
|
||||||||||||||
|
|
Примери:
Employee *boss = new Employee("John",
3200); // началник е John, * означава
дефиниране на указател
Employee
harry("Harry", 1500);
*boss =
harry;
// началник вече е Harry, * означава
операция *, lvalue;
// операция присвояване (Employee)
int *pn
= new int(10);
cout << *pn; //
10; операция *, стойността на оперцията е използвана като
rvalue
*pn = 12;
// операция *, стойността
на оперцията е lvalue; операция присвояване
(int)
cout << *pn;
// 12; операция *, стойността
на оперцията е използвана като rvalue
* Обекти и променливи без имена
Операция new създава обект без име, като достъпът до
този обект става с указатели.
Примери: Указатели boss
и h осигуряват дастъп до обекта Employee("John",
3200).
Employee
*boss = new Employee("John", 3200); //
*boss е John
Еmployee *h;
h = boss; //
операция присвояване (Employee*); *boss и *h е все John
boss = NULL; //
операция присвояване (Employee*); само *h е
John, *boss е грешка!
Примери:
delete boss; //
boss е указател със стойност адрес в динамичната памет
delete pn; //
pn е указател със стойност адрес в динамичната
памет
delete [] arr; // освобождава паметта, запазена с new (целия
масив)
-- Опит за
работа със стойност на указател след освобождаване на заетата
памет
Пример: След операция delete, pk става
невалиден указател.
int *pk = new int(10);
cout << *pk; // 10; операция *
delete pk; // операция delete
*pk = 100; // операция *, операция присвояване
(int); невалиден
указател
// на адрес pk няма променлива
тип int
Опасна грешка!
delete
pk; //
операция delete
pk = NULL;
*pk = 100; // ОС идентифицира грешката
-- Неопределена
(незаредена) променлива
Пример: Операция
* връща невалидна променлива.
int *pk;
*pk = 100; // невалиден
указател
// указателят
pk съдържа неопределена стойност (незаредена променлива)
Опасна грешка
int*
pk = NULL;
*pk = 100; // ОС идентифицира грешката
Пример: Адрес на променлива в динамичната
памет.
int *p = new int(20);
int *p1 = &(*p); // инициализира p1
с адреса на променливата *p
int *p2 = p; //
инициализира p2 с адреса, записан в p
Пример: Локални обект harry
и указател h, инициализиран с адреса на harry.
Employee harry("Harry", 4300); // обект в системния стек
Employee *h =
&harry;
// h съдържа адрес
на обект в системниея стек
int k = 10; int *pk = &k; cout << k; cout << (*pk); |
STACK Адрес XXX
|
STACK
|
Пример: Разменяне на стойностите на две променливи
// формалните параметри са
параметри-псевдоними
void swap1(int &a, int &b) //
разменя стойностите на променливите a и b
{
int w = a;
a = b;
b = w;
}
// формалните параметри са параметри-променливи от
тип указатели
void swap2(int *pa, int *pb) // разменя
стойностите на променливите с адреси указателите a и b
{
int w = *pa;
*pa = *pb;
*pb = w;
}
int main()
{
int x = 10, y = 12;
swap1(x, y); //
фактическите параметри са промелниви от тип int
cout << x << " " << y
<< endl;
swap2(&x, &y); //
фактическите параметри са адреси на променливи от тип int
cout << x << " " << y
<< endl;
return 0;
}
* Пример (optional attribute): Класът
Department представя департамент с име (тип string)
и администратор (тип Employee), като в някой департамент
може да няма администратор.
1. Решение без указатели с допълнителна данна
exists, която показва има ли администратор:
class Department {2. Решение с указател. Ако в департамента има администратор, то указателят ще сочи към този обект (ще съдържа адреса на този обект), ако няма - указателят ще съдържа NULL.
. . .
private:
string name;
bool exists; // true ако има администратор
Employee recept; // обектът съществува само ако има администратор
};
class Department {
. . .
private:
string name;
Employee* receptionist;
};
Пример (sharing): Някои
департаменти могат да имат администратор и/или секретар, като
някъде един и същи служител може да заема и двете длъжности.
class Department {
. . .
private:
string name;
Employee* receptionist; // NULL ако няма; адреса на администратора, ако има
Employee* secretary; // NULL ако няма; адреса на секретаря, ако има
};
// department.cpp
#include <string> #include <iostream> using namespace std; #include "ccc_empl.h" /**
A department in an organization. */ class Department { public: Department(string n); void set_receptionist(Employee* e); void set_secretary(Employee* e); void print() const; private: string name; Employee* receptionist; Employee* secretary; }; /** Constructs a department with a given name. @param n the department name */ Department::Department(string n) { name = n; receptionist = NULL; secretary = NULL; } /** Sets the receptionist for this department. @param e the receptionist */ void Department::set_receptionist(Employee* e) { receptionist = e; } /** Sets the secretary for this department. @param e the secretary */ void Department::set_secretary(Employee* e) { secretary = e; } /** Prints a description of this department. */ void Department::print() const { cout << "Name: " << name << "\n" << "Receptionist: "; if (receptionist == NULL) cout << "None"; else cout << receptionist->get_name() << " " << receptionist->get_salary(); cout << "\nSecretary: "; if (secretary == NULL) cout << "None"; else if (secretary == receptionist) cout << "Same"; else cout << secretary->get_name() << " " << secretary->get_salary();
cout << "\n"; } int main() { Department shipping("Shipping"); Employee* harry = new Employee("Hacker, Harry", 45000); shipping.set_secretary(harry);
Department qc("Quality Control");
Employee* tina = new Employee("Tester, Tina", 50000); qc.set_receptionist(tina); qc.set_secretary(tina);
tina->set_salary(55000);
Department cs("Computer Support");
Employee* mary = new Employee("Brown, Mary", 33000);
cs.set_receptionist(mary);
Employee* boby = new Employee("Hill, Boby", 44000);
cs.set_secretary(boby);
shipping.print(); qc.print();
cs.print();
return 0; }
nkirov@cpp % c++ department.cpp
ccc_empl.cpp nkirov@cpp % ./a.out Name: Shipping Receptionist: None Secretary: Hacker, Harry 45000 Name: Quality Control Receptionist: Tester, Tina 55000 Secretary: Same Name: Computer Support Receptionist: Brown, Mary 33000 Secretary: Hill, Boby 44000 |
* Името на масив е константен
указател. Променлива
указател може да служи за име на масив.
Пример:
int a[3] = {10, 20, 30};
int *pa = a;
// отпечатва 3 пъти стойността на a[0]
cout << a[0] <<"
"<< pa[0] <<" "<< *pa; // 10 10 10
* Адресна аритметика
Синтаксис: <указател> + <число>
Стойността на операцията е адрес, отместен с <число>
променливи от същия тип
Примери:
// отпечатва 4 пъти стойността на a[1]
cout << a[1] <<"
"<< pa[1] <<" "
<< *(pa+1) <<"
"<<(pa+1)[0]; // 20 20 20 20
Адрес | pa или a | pa+1 или a+1 | pa+2 или a+2 |
Стойност | 10 | 20 | 30 |
Индекс | 0 | 1 | 2 |
Достъп 1 | a[0] | a[1] | a[2] |
Достъп 2 | *pa | *(pa+1) | *(pa+2) |
Достъп 3 | pa[0] | (pa+1)[0] | (pa+1)[1] |
Достъп 4 | *a | *(a+1) | *(a+2) |
char s[] = "Harry"; // същото като
char *s = "Harry";
*s | *(s+1) | *(s+2) | *(s+3) | *(s+4) | *(s+5) |
s[0] |
s[1] | s[2] | s[3] | s[4] | s[5] |
'H' | 'a' | 'r' | 'r' | 'y' | '\0' |
char* p = "Harry";Превръщане на обекти от тип string в тип char*.
string name(p); // or string name = p;
string num = "123";
const char* cnum = num.c_str();
int n = atoi(cnum);