Работа С++девелопером
Введение в классы
Перегружаемые функции и операторы
Встраиваемые функции
Конструкторы и деструкторы
Обмен данными между объектами и приложением
Указатели и ссылки на объекты
Наследование в языке С++
Виртуальные функции
Динамическое управление памятью
Тестовые примеры
Введение в классы
Класс– это механизм для создания новых типов. Синтаксис описания класса похож на синтаксис описания структуры. В отличие от структур в языке СИ членами структуры в языке С++ кроме переменных могут быть и функции. В С++ для создания структуры (класса) традиционно принято использовать ключевое слово class, но допускается использовать и struct.
struct _3d { // public по умолчанию double mod (); //объяв_ся функция mod double projection (_3d r); private: // модификатор доступа double x, y, z; }; |
class _3d { double x, y, z; // private по умолчанию public: // модификатор доступа double mod (); double projection (_3d r); }; |
Функции объявлены при описании класса, но они еще не определены.
double _3d::mod () // определение функции mod класса _3d { return x + y +z;} double _3d::projection (_3d r) // объект r класса _3d {return x + r.mod();} //вызывается метод (функция) mod объекта r
Запуск программы с использованием переменных и функций созданного типа
#include <iostream> using namespace std; void main() { _3d a, b; //объявляются объекты a и b класса _3d a.x=1; a.y=1; a.z=1; //инициализация свойств объектов a и b b.x=2; b.y=2; b.z=2; double dPro, dMod; dMod = a.mod(); //вызывается функция mod объекта a dPro = b.projection(a); //вызывается функция projection(a) объекта b cout << dMod << "\n"; cout << dPro << "\n"; }
В этой и последующих обучающих программах есть ошибки, которые необходимо определить и исправить при отладке программы. Нахождение ошибок способствует усвоению материала
Контрольные задания. Создайте программу в проекте консольного приложения.и и запустите ее. Если в программах есть ошибки, исправьте их. Обратите внимание на следующее:
- описание класса должно предшествовать его использованию;
- к закрытым переменным и функциям можно обращаться только в определениях функций класса.
Прочитайте код программы, используя терминологию программиста (см. комментарии к программе).
Перегружаемые функции и операторы
Транслятор С++ различает функции не только по именам, но и по типу аргументов. В языке С++ можно «перегрузить» одно имя для трех типов данных.
int abs (int x) { return x<0 ? -x : x; } // определение функции 1 long abs (long x) { return x<0 ? -x : x; } // определение функции 2 double abs (double x) { return x<0 ? -x : x; } // определение функции 3 #include <iostream> using namespace std;main() { cout << abs(-1000) << "\n"; // вызов функции 1 cout << abs(-1000L) << "\n"; // вызов функции 2 cout << abs(-1000.0) << "\n"; // вызов функции 3 }
Одно из основных применений перегрузки функций – это достижение полиморфизма, что воплощает в себе философию «один интерфейс, множество методов«.
В языке VLISP нет различия между операциями и функциями. Операции в C\C++ также можно воспринимать как функции, которые отличаются лишь синтаксисом выражения. Язык С++ позволяет перегружать и операции.
Многие из операций выполняются над различными типами переменных – бесформатный ввод-вывод (<< и >>) или математические операции (+, -) и т. д. Пример:
int i1, i2, i3; long int l1, l2, l3; double f1, f2, f3; i1 = i2 + i3; l1 = l2 + l3; f1 = f2 + f3;
Пример перегрузки оператора «+» на действия с векторами:
struct _3d // описание класса _3d { double x, y, z; // координаты _3d operator + (_3d); // объяв-ся ф-я “operator +” c пар-м типа _3d и возвр. объект класса _3d }; _3d _3d::operator + (_3d b) // опред-ся ф-я “operator +” класса _3d { _3d c; c.x = x + b.x; c.y = y + b.y; c.z = z + b.z; return c; } #include <iostream> using namespace std; void main() { _3d A, B, C; A.x = 1.0; A.y = 2.0; A.z = 3.0; B = A; C = A + B; // тоже самое C = A.operator + (B); выз-ся ф-я "operator +" объекта A c пар_м B cout << C.x <<"\n"<< C.y <<"\n"<<C.z <<"\n"; }
Прочитайте код программы, используя терминологию программиста (см. комментарии к программе).
Встраиваемые функции
Встраиваемые функции рассмотрим на следующем примере фрагмента программы:
struct _3d { double mod (){return x + y +z;}; // 1-й способ встраивания (ф-я определена в классе) double projection (_3d r); private: double x, y, z; }; inline double _3d::projection (_3d r) // 2-й способ встраивания (ключевое слово inline) { return x + r.mod(); }
Встраиваемые функции действуют почти так же, как и директивы препроцессора, в том смысле, что тело функции вставляется в место вызова функции до компиляции. Замена вызова функции на ее определение ускоряет работу программы. Поэтому рекомендуется многократно используемым в цикле функциям давать статус встраиваемых (inline).
Кроме этого, inline функции обеспечивают более стройный способ встраивания в программу короткой функции (см. 1-й способ встраивания). Также, компилятор С++ гораздо лучше работает со встраиваемыми функциями, чем с директивами препроцессора и другими макроопределениями.
Конструкторы и деструкторы
Конструктор – метод, который вызывается автоматически при создании объекта.
Деструктор – метод, который вызывается автоматически при уничтожении объекта.
Для обоих имя совпадает с именем класса (для деструктора добавляется ~).
Обычно конструктор используется для создания и инициализации переменных объекта, деструктор – для очистки памяти от них. Пример:
struct _3d { double mod ( ) {return x ;}; _3d( ) {x=5;} // конструктор ~_3d( ){cout << "Destructor";} private: double x; }; main() { _3d A; // объявление объекта A и вызов конструктора _3d() cout << A.mod()<<"\n"; } // уничтожение объекта A и вызов деструктора ~_3d()
Результат работы программы:
5 Destructor
В классе может быть определено несколько конструкторов. Конструкторы могут иметь параметры. Пример:
class _3d { double x, y, z; public: double mod ( ) {return x ;}; _3d() { x=y=z=0;} _3d (double initX, double initY, double initZ) { x = initX; y = initY; z = initZ; } }; main() { _3d A; // A.x = A.y = A.z = 0; _3d B (3,4,0); // B.x = 3.0, B.y = 4.0, B.z = 0.0 cout << A.mod()<<"\n"; }
Контрольное задание. Еще до запуска программы определите, что выведется на печать? Выведите на печать переменную y объекта B.
Обмен данными между объектами и приложением
Обращение к закрытым переменным через открытые методы наподобие PublicMethod() входит в число стандартных приемов программирования; тем самым удается ограничить доступ к закрытым данным класса. Пример:
#include <iostream> using namespace std; class DataClass { private: int PrivateDataMember; // закрытая переменная public: DataClass(int Value); int PublicDataMember; int PublicMethod(void); // открытый метод }; DataClass::DataClass(int Value) { PrivateDataMember = Value; } int DataClass::PublicMethod(void) { return PrivateDataMember; } void main() { DataClass DataObject(1); DataObject.PublicDataMember = 2; cout << "DataObject.PublicDataMember = " << DataObject.PublicDataMember << "\n"; cout << "DataObject.PrivateDataMember = " << DataObject.PublicMethod() << "\n"; }
Контрольное задание. Соблюдая терминологию прочитайте последовательно программу от инициализации переменной PrivateDataMember до ее вывода на печать.
Указатели и ссылки на объекты
Доступ к членам объекта можно осуществлять и через указатель на объект. В этом случае применяется операция стрелка «->».
main() { _3d A (2,3,4); _3d *pA; pA = &A; cout << pA->mod()<<'\n'; // вызывается функция mod объекта, на который указывает указатель pa }
Объекты можно передавать в качестве аргументов функции точно так же, как передаются данные других типов. Пример:
class ClassName { public: ClassName (){cout << "Constructor \n";} ~ClassName (){cout << "Destructor \n";} }; void f (ClassName o) {cout << "Function \n";} main() { ClassName c1; f (c1); }
Эта программа напечатает следующее:
Constructor Function Destructor Destructor
Конструктор вызывается только один раз. Это происходит при объявлении объекта с1. При передаче объекта c1 функции f(c1) в качестве параметра в памяти создается копия объекта, однако конструктор при этом не вызывается. Деструктор же в программе вызывается дважды — один раз для копии, второй раз для самого объекта c1.
Тот факт, что конструктор вызывается один раз, а деструктор вызывается дважды, может стать потенциальным источником проблем. Поэтому, предпочитают использовать параметр-указатель или параметр-ссылку. По сути различие между первым и вторым лишь в синтаксисе написания.
Параметр-указатель
void ToZero (_3d *vec) { vec->set (0,0,0); // используется " -> " } main() { _3d A (2,3,4); ToZero (&A); } |
Параметр-ссылка
void ToZero (_3d &vec) { vec.set (0,0,0); // используется "." } main() { _3d A (2,3,4); ToZero (A); } |
Параметр-указатель и параметр-ссылку удобно использовать для обновления значения переменной, используемой в качестве входного параметра .
Параметры-ссылки имеют некоторые преимущества по сравнению с аналогичными альтернативными параметрами-указателями. Во-первых, нет необходимости получать и передавать в функцию адрес аргумента, адрес передается автоматически. Во-вторых, ссылки предлагают более понятный и элегантный интерфейс.
Контрольное задание: используя фрагмент программы (с указателями) довести ее до рабочего состояния с описанием класса _3D и выводом в окно консоли значение какой-либо переменной. При этом все функции и переменные поместить в описание класса _3D. Переменные сначала описать в классе открытыми (Public). Затем задание усложняется — переменные описать в классе закрытыми (Private)
Ссылки на объект могут также использоваться в качестве возвращаемого значения функции. Обычно такой механизм применяется в сочетании с указателем this. Рассмотрим пример перегрузки оператора «=»:
_3d& _3d::operator = (_3d &b) // Прочитайте этот рядок { x = b.x; y = b.y; z = b.z; return *this; }
Возвращаемый результат этой функции будет тот объект, который вызывал операцию «=».
Наследование в языке С++
Пример 1:
#include <iostream> using namespace std; class vehicle { public: void start(); void move(); }; class helicopter : public vehicle { // описывается класс, производный от базового public: void move(); }; void vehicle::start() { cout << "Starting...\n";} void vehicle::move() { cout << "Driving...\n";} // определяется метод move() void helicopter::move() { cout << "Flying...\n";} // переопределяется метод move() в производном классе void main() { helicopter whirly; whirly.start(); whirly.move(); // выводится "Flying", а не "Driving" }
В этой программе демонстрируется основное правило наследования «не нашел у себя, можешь позаимствовать у отца». При вызове whirly.start() функция была позаимствована у «отца» (базовом классе), а функция whirly.move() была переопределена в производном классе. Какая из 2-х функций move() вызывается, определяется по имени объекта, к которому она отнесена. Здесь мы вновь сталкиваемся с перегрузкой функций.
Пример 2:
#include <iostream> using namespace std; class BaseClass { public: BaseClass() {cout << "Base class constructor \n";} ~BaseClass() {cout << "Base class destructor \n";} }; class DerivedClass: public BaseClass // public – модификатор наследования { public: DerivedClass() {cout << "The constructor of the derived class \n";} ~DerivedClass() {cout << "The destructor of the derived class \n";} }; main() { DerivedClass obj; }
Эта программа выводит сообщения в следующей последовательности:
Base class constructor The constructor of the derived class The destructor of the derived class Base class destructor
Модификатор наследования (public, protected или private) определяет, какие права доступа к переменным и методам класса-родителя будут «делегированы» классу-потомку. Protected эквивалентен private с единственным исключением: protected члены базового класса доступны для членов всех производных классов.
При реализации наследования возможна передача параметров из конструктора производного к конструктору базового класса, при этом допускается, чтобы конструктор базового класса имел больше параметров, чем конструктор производного класса.
#include <iostream> using namespace std; class BaseClass { int j, i; public: BaseClass (int jj, int ii) // 2. Конструктор базового класса { j=jj; i=ii; cout << j << "\n" << i << "\n"; } }; class DerivedClass: public BaseClass { int n; public: DerivedClass (int nn); }; DerivedClass :: DerivedClass (int nn): BaseClass (nn/2, nn%2) // 3. Конструктор производного класса { n=nn; } main() { DerivedClass obj(15); // 1. Объявляется объект производного класса }
В результате работы программы на экран выведутся числа 7 и 1.
На этом этапе обучения обычно возникают сложности с правильным чтением программы, поскольку накопилось слишком много синтаксических особенностей, которые сложно различать новичкам. «Пирамидка знаний» еще неустойчива и как бы «рассыпается». Нужно проявить упорство и успех придет.
Программа читается в последовательности, указанной пунктами: 1, 2, 3. После объявления объекта (1) вызывается конструктор базового класса (2), а потом производного (3). Передача 2-х параметров конструктору базового класса обусловлена синтаксисом определения конструктора базового класса (3)
Виртуальные функции
Ниже рассмотрен пример наследования класса Circle от класса Point.
//Объявление enum //задает тип переменной перечисления и //определяет список именованных констант enum Boolean {false, true}; // false = 0, true = 1 class Point { protected: int X; int Y; Boolean Visible; public: Point (const Point& cp); Point (int newX =0, int new Y =0); void Show(); void Hide(); void MoveTo(int newX, int newY); }; void Point::Show() { Visible = true; putpixel (X,Y,getcolor()); } void Point::Hide() { Visible = false; putpixel (X,Y,getbkcolor()); } void Point::MoveTo (int newX, int newY) { Hide (); X = newX; Y = newY; Show (); } main() { int graphDr = DETECT, graphMode; initgraph ( &graphDr, &graphMode, ""); Point pointA (50,50); pointA.Show (); pointA.MoveTo (100,130); pointA.Hide (); closegraph(); } |
class Circle: public Point { int Radius; // private по умолчанию public: Circle (int initX, int initY, int initR); void Show (); void Hide (); void MoveTo (int newX, int newY); }; Circle::Circle (int initX, int initY, int initR) :Point (initX, initY) { Radius = initR; } void Circle::Show () { Visible = true; circle (X,Y, Radius); } void Circle::Hide () { Visible = false; unsigned int tempColor = getcolor (); setcolor (getbkcolor()); circle (X,Y, Radius); setcolor (tempColor); } void Circle::MoveTo (int newX, int newY) { Hide (); X = newX; Y = newY; Show (); } main() { int graphDr = DETECT, graphMode; initgraph ( &graphDr, &graphMode, ""); Circle C (150,200,50); C.Show(); C.MoveTo (300,100); getch(); closegraph(); } |
Обратите внимание на одинаковые по содержанию функции (методы).
void Point::MoveTo (int newX, int newY) //функция отца void Circle::MoveTo (int newX, int newY) //функция сына
Уберем вторую из программы, ведь есть правило «не нашел у себя, можешь позаимствовать у отца». Тогда обращение к функции через объект производного класса C.MoveTo (300,100) вызовет функцию базового класса Point::MoveTo (int newX, int newY), а из нее запустятся функции Point::Hide() и Point::Show() в соответствии с тем же правилом. Однако такое правило в этой ситуации не подходит. По логике программы необходимо, чтобы из функции отца Point::MoveTo (int newX, int newY) запустились функции сына Circle::Hide() и Circle::Show(). Как решить эту проблему? Необходимо объявить эти функции виртуальными в обоих классах:
virtual void Hide ();
virtual void Show ();
Правило для виртуальных методов. Если функция объявлена виртуальной и разработчик переопределил ее в производном классе, то при вызове виртуальной функции из некоторой функции базового класса может быть вызвана как функция объекта производного класса так и базового класса. Чем определяется выбор? Типом объекта, через который реализуется обращение к функции.
Теперь, из функции отца Point::MoveTo (int newX, int newY) будут вызываться виртуальные функции производного класса Circle::Hide() и Circle::Show(), если было обращение к функции MoveTo реализуется через объект производного класса: C.MoveTo (300,100) . В случае обращения к функции MoveTo через объект базового класса pointA.MoveTo (300,100) из функции отца Point::MoveTo (int newX, int newY) будут вызываться виртуальные функции базового класса Point::Hide() и Point::Show()
Связь с виртуальной функцией устанавливается не во время компиляции, а во время запуска программы — когда объект, к которому она отнесена, уже создан. Несмотря на гибкость работы виртуальных методов, не создавайте их без необходимости, поскольку для обеспечения быстродействия будет лучше, если все связи устанавливаются во время компиляции, а не во время выполнения программы.
Для чего нужны виртуальные функции? Их применение, отнюдь, не ограничивается экономией программного кода, как в рассмотренном примере. Наиболее широко механизм виртуальных функций используется в подключаемых к программе библиотеках классов, например — библиотеке MFC (MicrosoftFoundationClasses). Из методов MFC классов часто предусматривается вызов функций, которые еще не определены. Они определяются в создаваемых программистом производных классах.
Динамическое управление памятью
В программе на языке Си память динамически выделяется функциями malloc и calloc. Функция free (p) освобождает область памяти, на которую ссылается указатель p. Но при создании объекта в С++ используется конструктор. А функция malloc (), разработанная задолго до появления на свет С++, понятия не имеет о конструкторах.
В языке С++ для выделения памяти используется оператор new , который не только выделяет память, но и вызывает соответствующий конструктор:
CStatic* MyStatic; // Указатель на объект надпись MyStatic = new CStatic(); if (MyStatic!=NULL) MyStatic->Create("MyStatic",WS_CHILD|WS_VISIBLE|SS_CENTER, CRect(10,10,100,50),this);
Параметры, указанные после имени объекта, передаются конструктору.
Когда необходимость в объекте, созданном оператором new, отпадет, освободиться от него можно с помощью оператора delete.
if (MyStatic!=NULL) delete MyStatic; // удалить динамический объект
Если этого не сделать, то объект удалится только после окончания работы приложения.
Тестовые примеры
Ниже приводятся программы и фрагменты программ. Для тестового примера необходимо определить задачу, выполняемую программой в целом и каждой строкой в отдельности, знать последовательность обращения к операторам, а также уметь правильно комментировать назначение элементов языка, используемых в программном коде. Чтение строк обычно начинается со слов «объявляется, определяется, вызывается, инициализируется …» и т.п.
struct _3d { double mod (); double projection (_3d r); private: double x, y, z; }; double _3d::mod () { return x + y +z; } double _3d::projection (_3d r) { return x + r.mod(); } #include <iostream> using namespace std; void main() { _3d a, b; a.x=1; a.y=1; a.z=1; b.x=2; b.y=2; b.z=2; double dPro, dMod; dMod = a.mod(); dPro = b.projection(a); cout << dMod << "\n"; cout << dPro << "\n"; }
int abs (int x) { return x<0 ? -x : x; } long abs (long x) { return x<0 ? -x : x; } double abs (double x) { return x<0 ? -x : x; } #include <iostream.h> main() { cout << abs(- 1000 ) << "\n"; cout << abs(- 1000L ) << "\n"; cout << abs(- 1000.0 ) << "\n"; }
struct _3d { double x, y, z; // координаты _3d operator + (_3d); }; _3d _3d:: operator + (_3d b) { _3d c; c.x = x + b.x; c.y = y + b.y; c.z = z + b.z; return c; } #include <iostream> using namespace std; void main() { _3d A, B, C; A.x = 1.0; A.y = 2.0; A.z = 3.0; B = A; C = A + B; cout << C.x <<"\n"<< C.y <<"\n"<<C.z <<"\n"; }
struct _3d { double mod () {return x + y +z;} ; double projection (_3d r); private: double x, y, z; }; inline double _3d::projection (_3d r) { return x + r.mod(); }
struct _3d { double mod ( ) {return x ;}; _3d( ) {x=5;} ~_3d( ){cout << "End";} private: double x; }; main() { _3d A; cout << A.mod()<<"\n"; }
struct _3d { double mod ( ) {return x;}; void set (double initX) { x = initX;} private: double x; }; main() { _3d A; A.set (5); cout << A.mod()<<"\n"; }
class _3d { double x, y, z; public: double mod ( ) {return x;}; _3d() { x=y=z=0;} _3d (double initX, double initY, double initZ) { x = initX; y = initY; z = initZ; } }; main() { _3d A; _3d B (3,4,0); cout << A.mod()<<"\n"; }
#include <iostream> using namespace std; class DataClass { private: int PrivateDataMember; public: DataClass(int Value); int PublicDataMember; int PublicMethod(void); }; DataClass::DataClass(int Value) { PrivateDataMember = Value; } int DataClass::PublicMethod(void) { return PrivateDataMember; } void main() { DataClass DataObject(1); DataObject.PublicDataMember = 2; cout << "DataObject.PublicDataMember = " << DataObject.PublicDataMember << "\n"; cout << "DataObject.PrivateDataMember = " << DataObject.PublicMethod() << "\n"; }
main() { _3d A (2,3,4); _3d *pA; pA = &A; cout << pA->mod() <<"\n"; }
class ClassName { public: ClassName (){cout << "Работа конструктора \n";} ~ClassName (){cout << "Работа деструктора \n";} }; void f (ClassName o) {cout << "Работа функции f \n";} main() { ClassName c1; f (c1); }
void ToZero (_3d *vec ) { vec->set (0,0,0); } main() { _3d A (2,3,4); ToZero ( &A ); }
void ToZero (_3d &vec ) { vec.set (0,0,0); } main() { _3d A (2,3,4); ToZero ( A ); }
3d& _3d::operator = (_3d &b) { x = b.x; y = b.y; z = b.z; return *this; }
#include <iostream> using namespace std; class BaseClass { public: BaseClass() {cout << "Base class constructor \n";} ~BaseClass() {cout << "Base class destructor \n";} }; class DerivedClass: public BaseClass // public – модификатор наследования { public: DerivedClass() {cout << "The constructor of the derived class \n";} ~DerivedClass() {cout << "The destructor of the derived class \n";} }; main() { DerivedClass obj; }
#include <iostream> using namespace std; class BaseClass { int j, i; public: BaseClass (int jj, int ii) { j=jj; i=ii; cout << j << "\n" << i << "\n"; } }; class DerivedClass: public BaseClass { int n; public: DerivedClass (int nn); }; DerivedClass :: DerivedClass (int nn): BaseClass (nn/2, nn%2) { n=nn; } main() { DerivedClass obj(15); }
enum Boolean {false, true}; // false = 0, true = 1 class Point { protected: int X; int Y; Boolean Visible; public: Point (const Point& cp); Point (int newX =0, int new Y =0); void Show(); void Hide(); void MoveTo(int newX, int newY); }; void Point::Show() { Visible = true; putpixel (X,Y,getcolor()); } void Point::Hide() { Visible = false; putpixel (X,Y,getbkcolor()); } void Point::MoveTo (int newX, int newY) { Hide (); X = newX; Y = newY; Show (); } main() { int graphDr = DETECT, graphMode; initgraph ( &graphDr, &graphMode, ""); Point pointA (50,50); pointA.Show (); pointA.MoveTo (100,130); pointA.Hide (); closegraph(); } |
class Circle: public Point { int Radius; // private по умолчанию public: Circle (int initX, int initY, int initR); void Show (); void Hide (); void MoveTo (int newX, int newY); }; Circle::Circle (int initX, int initY, int initR) :Point (initX, initY) { Radius = initR; } void Circle::Show () { Visible = true; circle (X,Y, Radius); } void Circle::Hide () { Visible = false; unsigned int tempColor = getcolor (); setcolor (getbkcolor()); circle (X,Y, Radius); setcolor (tempColor); } void Circle::MoveTo (int newX, int newY) { Hide (); X = newX; Y = newY; Show (); } main() { int graphDr = DETECT, graphMode; initgraph ( &graphDr, &graphMode, ""); Circle C (150,200,50); C.Show(); C.MoveTo (300,100); getch(); closegraph(); } |
CStatic* MyStatic; MyStatic = new CStatic(); if (MyStatic!=NULL) MyStatic->Create("MyStatic",WS_CHILD|WS_VISIBLE|SS_CENTER, CRect(10,10,100,50),this); if (MyStatic!=NULL) delete MyStatic;
#include <iostream> using namespace std; class vehicle { public: void start(); void move(); }; class helicopter : public vehicle { public: void move(); }; void vehicle::start() { cout << "Starting...\n";} void vehicle::move() { cout << "Driving...\n";} void helicopter::move() { cout << "Flying...\n";} void main() { helicopter whirly; whirly.start(); whirly.move(); }
Пример 19:
#include <iostream> using namespace std; #include <stdio.h> class TIP { public: int a,b; virtual f2(int b, int pb); int *pa, *pb; TIP(int per); ~TIP(); f1(int a, int pa); }; class tip1:public TIP{ public: tip1(int per):TIP(per){ }; f2(); }; TIP::TIP (int per) { a = per; } TIP::~TIP() { printf("Destructor\n"); } TIP::f1(int a, int pa) { printf("a=%d,\n&a=%d\n",a,pa); } TIP::f2(int b, int pb) { printf("b=%d,\n%&b=%d\n",b,pb); } tip1::f2() { printf("pro_klassn\n"); } void main() { TIP CC(2); TIP *pc = &CC; //TIP *pc; pc=&CC; tip1 CC1(4); printf("adr_CC=%d\n",pc); pc->pa = &CC.a; pc->b = pc->a * 3; pc->pb = &CC.b; pc->f1(pc->a,(int)CC.pa); CC.f2(pc->b,(int)CC.pb); CC1.b= CC1.a*3; CC1.pb=&CC1.b; pc->f2(CC1.b,(int)CC1.pb); pc =&CC1; //CC.~TIP(); printf("adr_CC1=%d\n",pc); // //pc->f2(); CC1.f2(); //CC1.~tip1(); }
Автор: Николай Свирневский