//
static2.cpp
//
demonstrates static data and functions
//
uses constructor to initialize one widget
//
uses also a copy constructor
#include
<iostream.h>
#include
<conio.h>
using
namespace std;
////////////////////////////////////////////////////////////////
class
widget
{
private:
int widget_number;
// a widget's serial number
static int total_widgets; // all widgets made so far
// NOTE: declaration only
public:
widget()
// constructor that initialize one widget
{
widget_number = 10000 + total_widgets++;
}
widget(widget & wg)
// copy constructor
{
widget_number = 10000 + total_widgets++;
}
int get_number()
// get a widget's number
{
return widget_number;
}
static int get_total() // get total widgets
{
return total_widgets;
}
};
// class widget
int widget::total_widgets = 0; // NOTE: definition
void
main()
{
cout << "Total widgets = " << widget::get_total() <<
endl;
widget w1; // create widgets
cout << "Total widgets = " << widget::get_total() <<
endl;
widget w2; // create widgets
cout << "Total widgets = " << widget::get_total() <<
endl;
widget w3(w1); // create widgets
cout << "Total widgets = " << widget::get_total() <<
endl;
cout << "w1 = " << w1.get_number() << endl;
cout << "w2 = " << w2.get_number() << endl;
cout << "w3 = " << w3.get_number() << endl;
// added to stop the screen
getch();
}
Единствената промяна в старата версия е добавянето на копиращ конструктор в дефиницията на класа:
widget(widget & wg)
// copy constructor
{
widget_number = 10000 + total_widgets++;
}
Това води до правилния изход от програмата
Total
widgets = 0
Total
widgets = 1
Total
widgets = 2
Total
widgets = 3
w1
= 10000
w2
= 10001
w3
= 10002
Ето още една програма, подобрена версия на програмата от предишната лекция copycon.cpp, взета от превъзходния курс на Лафоор [1]. В нея се наименоват и номерират обектите, както и се отчита техният брой. Класът omega има две променливи, които характеризират обектите - име, name, и номер, number. Също така има една статична константа, която е необходима за определяне на размера на масива name, така както това бе обяснено в т. 4 на лекция 34. За да се отчита броят на създадени обекти е дефинирана статичната променлива total, която се инициализира с нула след кода на класа.
//
copycon2.cpp
//
demonstrates copy constructors,
//
using objects that number themselves
#include
<iostream.h>
#include
<string.h> // for strncpy()
#include
<conio.h> // for getch()
class
omega
{
private:
static const int size = 20;
char name[size];
static int total;
int number;
public:
// one-arg constructor
omega(char str[]) : number(++total)
{
strncpy(name, str, size);
name[size - 1] = '\0';
cout << "\nI am the 1-arg constructor. I have "
<< "created object " << name << "-" << number;
}
// copy constructor
omega(const omega & om)
{
strncpy(name, om.name, size);
name[size - 1] = '\0';
number = ++total;
cout << "\nI am the copy constructor. I have "
<< "created object " << name << "-" << number;
}
void display()
{ cout << "I am object " << name << "-" << number;
}
};
// class omega
//
static int total;
int
omega::total = 0;
void
main()
{
omega om1("wrench"); // uses one-arg
constructor
omega om2 = om1;
// uses copy constructor
omega om3(om1);
// uses copy constructor
cout << endl;
cout << "\nom1: ";
om1.display();
cout << "\nom2: ";
om2.display();
cout << "\nom3: ";
om3.display();
getch();
}
// main()
В двата конструктора се принтират съобщения на екрана за тях когато те се извикват. Първият едноаргументен конструктор инициализира обект с дадено име и повишава броят на обектите (брои ги с променливата total) като предава този брой на номера на обекта с оператора number(++total) в инициализиращия списък на конструктора.
В копиращия конструктор се копира името на обекта и отново се повишава броят на обектите (брои ги с променливата total) като предава този брой на номера на обекта с оператора number(++total), но не в инициализиращия списък на конструктора, а в тялото на конструктора. Компилаторът на Борланд не забранява копиращия конструктор да има инициализиращ списък, т.е. той да изглежда от вида
omega(const omega & om) : number(++total)
{
strncpy(name, om.name, size);
name[size - 1] = '\0';
cout << "\nI am the copy constructor. I have "
<< "created object " << name << "-" << number;
}
но изглежда Лафоор е искал да подчертае разликата между двата конструктора, където при първия наистина се използва името при инициализацията на обекта (т.е. това, дадено при неговото създаване).
В главната функция се създава един обект, om1, от класа omega, а след това неговото съдържание се копира в два други обекта - om2 и om3.
Изходът от програмата е следният:
I
am the 1-arg constructor. I have created object wrench-1
I
am the copy constructor. I have created object wrench-2
I
am the copy constructor. I have created object wrench-3
om1:
I am object wrench-1
om2:
I am object wrench-2
om3:
I am object wrench-3
! Нова функция и една защита: обърнете внимание на двата реда
strncpy(name, om.name, size);
name[size - 1] = '\0';
Тази функция е подобна на strcpy, но копира в случая size на брой символа от om.name в променливата name. В оригинала на програмата в [1] вторият ред го няма, което води до грешка, ако името е по-голямо от size-1 символа. Това може да се поправи с втория ред, който слага като последен символ нулевия символ, '\0'. Всъщност, по-правилно е да се запише, че се копират само size - 1 символа както е следният ред (защото така и така, последният символ в името, този с номер size - 1, го заменяме с нулевия символ):
strncpy(name, om.name, size - 1);
name[size - 1] = '\0';
2. Извикване на копиращ конструктори от функциите. При предаване на аргументите на функция по стойност се извиква копиращите им конструктори. Също така при връщане на стойност от дадена функция също се извиква копиращ конструктор. И двата случая най-лесно могат да бъдат разбрани като се илюстрират с примери.
В следващата програма copycon3.cpp се извиква копиращ конструктор при предаване на аргумента og на функцията func, понеже компилаторът създава ново копие на обекта og.
//
copycon3.cpp
//
demonstrates copy constructors
//
passes objects to functions by value
#include
<iostream.h>
#include
<string.h> //
for strncpy()
#include
<conio.h>
// for getch()
class
omega
{
private:
static const int size = 20; // array size
char name[size];
// array for name string
static int total;
// total number of omegas
int number;
public:
// one-arg constructor
omega(char str[]) : number(++total)
{
strncpy(name, str, size-1);
name[size-1] = '\0';
cout << "\nI am the 1-arg constructor. I have "
<< "created object " << name << "-" << number;
}
// copy constructor
omega(const omega & om)
{
strncpy(name, om.name, size-1);
number = ++total;
cout << "\nI am the copy constructor. I have "
<< "created object " << name << "-" << number;
}
};
// class omega
int omega::total = 0;
void
main()
{
void func(omega); // declaration
omega om1("wrench"); // uses one-arg constructor
omega om2("screw");
func(om1);
// call the function
func(om2);
// call it again
getch();
}
// main()
void
func(omega og) //
argument passed by value
{
}
В главната функция се създават два обекта, om1 и om2, които се предават на функцията func (тя се извиква два пъти). В резултат се получават два "клонинга" на създадените обекти.
Забележете, че функцията func нищо не върши и това кара компилаторът да "се оплаче".
Parameter 'og' is never used.
Изходът от програмата е следният:
I
am the 1-arg constructor. I have created object wrench-1
I
am the 1-arg constructor. I have created object screw-2
I
am the copy constructor. I have created object wrench-3
I
am the copy constructor. I have created object screw-4
!Тук е уместно да се върнем към въпроса (на който не бяхме дали отговор) защо копиращият конструктор изисква предаването на аргумента си по връзка. Очевидно, ако той се предава по стойност, когато се извиква конструктора ще се създава нов обект с копиране и това ще довежда до извикване на конструктора, който ще създава нов обект и т.н. и т.н. до безкрай, по-точно, докато се изчерпи паметта от стека (която памет се използва за автоматичните променливи на функциите).
По същия начин, когато функцията връща по стойност също се извиква копиращия конструктор. Ето една кратка програма, която илюстрира този момент:
//
copycon4.cpp
//
demonstrates copy constructors
//
function returns object by value
#include
<iostream.h>
#include
<string.h> //
for strncpy()
#include
<conio.h>
// for getch()
class
omega
{
private:
static const int size = 20; // array size
char name[size]; // array for name
string
static int total; // total number of omegas
int number;
public:
// one-arg constructor
omega(char str[]) : number(++total)
{
strncpy(name, str, size-1);
cout << "\nI am the 1-arg constructor. I have "
<< "created object " << name << "-" << number;
}
// copy constructor
omega(const omega& om)
{
strncpy(name, om.name, size-1);
number = ++total;
cout << "\nI am the copy constructor. I have "
<< "created object " << name << "-" << number;
}
};
int omega::total = 0;
void
main()
{
omega retfunc(); // function declaration
retfunc(); // call the function
getch();
}
// main()
omega
retfunc() //
return by value
{
omega temp("bolt"); // uses one-arg constructor
return temp; // uses
copy constructor
}
В главната функция се извиква функцията retfunc(), която създава в тялото си един обект temp от класа omega. След това с копиращ конструктор създава още един обект, който се връща - затова изходът от тази програма е следният:
I
am the 1-arg constructor. I have created object bolt-1
I
am the copy constructor. I have created object bolt-2
Ако в главната функция дефинираме един обект om от класа omega, се извиква и безаргументният конструктор. Създадените обекти са общо три. Това е илюстрирано с програмата по-долу:
//
copycon4b.cpp
//
demonstrates copy constructors
//
function returns object by value
#include
<iostream.h>
#include
<string.h> //
for strncpy()
#include
<conio.h>
// for getch()
class
omega
{
private:
static const int size = 20; // array size
char name[size]; // array for name
string
static int total; // total number of omegas
int number;
public:
// no-arg constructor
*omega() : number(++total)
{
strncpy(name, "OMEGA", size-1);
cout << "\nI am the 0-arg constructor. I have "
<< "created object " << name << "-" << number;
}
// one-arg constructor
omega(char str[]) : number(++total)
{
strncpy(name, str, size);
cout << "\nI am the 1-arg constructor. I have "
<< "created object " << name << "-" << number;
}
// copy constructor
omega(const omega& om)
{
strncpy(name, om.name, size);
number = ++total;
cout << "\nI am the copy constructor. I have "
<< "created object " << name << "-" << number;
}
};
int omega::total = 0;
void
main()
{
omega retfunc(); // function
declaration
omega om;
om = retfunc();
// call the function
getch();
}
// main()
omega
retfunc()
// return by value
{
omega temp("bolt"); // uses one-arg constructor
return temp;
// uses copy constructor
}
Всъщност, промяната в тази програма са двата реда
omega om;
om = retfunc();
// call the function
и допълнително написания безаргументен конструктор, който е необходим за създаване на обекта om с твърдението omega om;:
omega() : number(++total)
{
strncpy(name, "OMEGA", size);
cout << "\nI am the 0-arg constructor. I have "
<< "created object " << name << "-" << number;
}
Спомнете си, че функцията не може да връща по връзка автоматични променливи от вида на temp по-горе, т.е. не може да запишем:
omega
& retfunc()
// return by reference IS IMPOSSIBLE!
{
omega temp("Pandora"); // uses one-arg constructor
return temp;
// uses copy constructor
}
.
Keywords: С++,
OOP programming , C++ , Classes , Inheritance , Reusability , Creating
New Data Types , Polymorphism and Overloading
Ключови думи: клас
, обект, обектно ориентирано програмиране , полиморфизъм switch if else
?