
техпрог / Comp-Sci-05
.pdf
Материалы к лекции 5.
Объектно-ориентированное программирование
Конструктор копирования
Имеется подставляемый системой конструктор копирования, он не игнорируется даже, если в классе уже определены конструкторы. Причѐм, не важно, есть у класса конструкторы по умолчанию или нет.
Пример.
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-2";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;} int get_speed(){return speed;} void set_year(int y){year=y;} int get_year(){return year;}
};
int main()
{Cdrom acer(48,2007); acer.set_speed(50); acer.set_year(2008);
Cdrom lg=acer;//инициализация с помощью существующего объекта cout<<"\nCd-Rom Acer "<<acer.get_speed()<<acer.get_year(); cout<<"\nCd-Rom LG "<<lg.get_speed()<<lg.get_year()<<endl;
cout<<"&acer= "<<&acer<<" &lg= "<<≶ return 0;
}
Можем определить собственный конструктор копирования. Конструктор копирования содержит один параметр, имеющий тип этого же класса. Этот параметр необходимо передавать по ссылке const, но не по значению, поскольку в этом случае получили бы бесконечную рекурсивную последовательность вызовов этого конструктора.
Пример.
#include <iostream> using namespace std; class Cdrom{
int speed;//скорость int year;// год выпуска
public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";}
~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}int get_speed(){return speed;}

void set_year(int y){year=y;} |
int get_year(){return year;} |
};
int main()
{Cdrom acer;
acer.set_speed(48); acer.set_year(2007);
Cdrom lg=acer;//инициализация с помощью существующего объекта Cdrom sony(acer); //другая форма вызова конструктора копирования cout<<"\nCd-Rom LG "<<lg.get_speed()<<lg.get_year(); cout<<"\nCd-Rom Sony "<<sony.get_speed()<<sony.get_year()<<endl; cout<<"&acer= "<<&acer<<" &lg= "<<&lg<<" sony= "<<&sony;
return 0;
}
Замечание. Синтаксис инициализации и конструктора копирования в C++ распространен на встроенные типы, хотя, в данном случае конструкторы не вызываются.
#include <iostream> using namespace std; int main()
{int x(100); //x=100 int y(x); //y=x
cout<<"x= "<<x<<"y= "<<y<<endl;
cout<<"&x= "<<&x<<" &y= "<<&y;//адреса разные! int z(200+x*y);
cout<<"\nz= "<<z<<endl; return 0;
}
Конструктор преобразования
Конструктор преобразования – это конструктор с одним параметром какого-то другого типа (не как у атрибутов класса).
Пример.
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";}
Cdrom(double x){speed=(int)x;year=2004;}//конструктор преобразования
~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}int get_speed(){return speed;} void set_year(int y){year=y;} int get_year(){return year;}

};
int main()
{Cdrom acer(52,2004);
Cdrom nec=31.2;
cout<<"\nCd-Rom NEC "<<nec.get_speed()<<nec.get_year(); return 0;
}
Пример. Конструктор преобразования. Объект acer создаѐтся конструктором Constr-1, объект Sony создаѐтся конструктором преобразования Constr-2.
#include <iostream>
using |
namespace std; |
class |
Cdrom{ |
|
int speed; int year; char marka[20]; |
public:
Cdrom(int s,char *m, int y=2002) {speed=s; strcpy(marka,m); year=y; cout<<"\nConstr-1";}
Cdrom (char *x) {speed=40; year=2004; strcpy(marka,x); cout<<"\nConstr-2";}
~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}int get_speed(){return speed;}
void set_year(int y){year=y;} |
int get_year(){return year;} |
}; |
|
int main() |
|
{
Cdrom acer(52,"Acer++",2008); Cdrom sony("Sony");
cout<<"\nCd-Rom sony "<<sony.get_speed()<<'\t'<<sony.get_year()<<endl; return 0;
}
Пример. Объявление Cdrom maxi=acer.Sravnim(nec); приведѐт к вызову метода Sravnim(), а затем конструктора копирования Constr-3. Объекты acer и maxi имеют разные адреса.
#include <iostream> using namespace std; class Cdrom{

int speed; int year; public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-1";}
Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}int get_speed(){return speed;}
void set_year(int y){year=y;} |
int get_year(){return year;} |
Cdrom Sravnim(Cdrom &p){cout<<"Sravnim()"; |
if (speed<p.speed) return p;
else return *this; }//можно было бы с помощью оператора ?
}; |
|
int main() |
|
{ |
|
Cdrom acer(52,2008); |
Cdrom nec(32,2001); |
Cdrom maxi=acer.Sravnim(nec);
cout<<"\nCd-Rom maxi "<<maxi.get_speed()<<'\t'<<maxi.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &maxi= "<<&maxi;
return 0;
}
Замечание. Могли бы использовать заголовок Cdrom Sravnim(const Cdrom &p), т. к. аргумент функции не изменяется. Отметим, что функция возвращает копию объекта.
Пример. Возвращаем объект с помощью указателя. Адреса у объектов maxi и acer одинаковые. Конструктор копирования не вызывается.
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}int get_speed(){return speed;} void set_year(int y){year=y;} int get_year(){return year;}
Cdrom * Sravnivaem(Cdrom &p){return (speed<p.speed) ? &p : this;}
};
int main()
{
Cdrom acer(52,2008); Cdrom nec(32,2001);
Cdrom * maxi=acer.Sravnivaem(nec);
cout<<"\nCd-Rom maxi "<<maxi->get_speed()<<'\t'<<maxi->get_year(); cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" maxi= "<<maxi;
// у maxi и acer адреса одинаковые! return 0;
}

Замечание. Если изменить заголовок функции Sravnivaem() на
Cdrom * Sravnivaem(const Cdrom &p)
то при вызове
Cdrom * maxi=acer.Sravnivaem(nec);
получим сообщение об ошибке, хотя параметр–объект и не изменяется функцией. Компилятор не может выполнить преобразование из 'const class Cdrom *' в 'class Cdrom *', поскольку функция имеет возвращаемое значение Cdrom *. Необходимо преобразование типов, которое можно выполнить с помощью const_cast (аннулирует действие модификатора const, т. е. указатель на постоянный объект преобразуется в указатель на непостоянный объект). Можем исправить функцию Sravnivaem() следующим образом:
Cdrom * Sravnivaem(const Cdrom &p)
{return (speed<p.speed) ? const_cast<Cdrom *>(&p) : this;}
или
Cdrom * Sravnivaem(const Cdrom &p)
{return (speed<p.speed) ? (Cdrom *)(&p) : this;}
Пример. Возвращаем объект с помощью ссылки. Адреса у объектов maxi и acer одинаковые. Конструктор копирования не вызывается.
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";}
~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;} int get_speed()const{return speed;} void set_year(int y){year=y;} int get_year()const{return year;}
Cdrom & Sravnenie(Cdrom &p) {return (speed<p.speed) ? p : *this; }
};
int main()
{
Cdrom |
acer(52,2008); |
Cdrom nec(42,2005); |
Cdrom |
& maxi=acer.Sravnenie(nec); |
cout<<"\nCd-Rom maxi "<<maxi.get_speed()<<'\t'<<maxi.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &maxi= "<<&maxi;
return 0;
}

Если заменить
Cdrom & maxi=acer.Sravnenie(nec);
на
Cdrom maxi=acer.Sravnenie(nec);
то будет вызван конструктор копирования и адреса у объектов maxi и acer будут уже разными.
Пример. Изменим заголовок функции Sravnenie(), добавив const.
const Cdrom & Sravnenie(const Cdrom &p) const
{return (speed<p.speed) ? p : *this; }
Ключевое слово const у параметра функции указывает, что функция не вносит изменений в объект, переданный как параметр (в примере это nec). Поскольку функция возвращает ссылку на один из объектов (вызвавший функцию или переданный через параметр), const нужно поставить и в возвращаемый тип – иначе будет сообщение об ошибке. Ключевое слово в конце заголовка означает, что не изменяется объект, вызвавший функцию (в примере это acer).
Отметим, что в этом примере, при создании объекта maxi будет вызван конструктор копирования, адреса у maxi и acer разные.
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s;year=y;cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}int get_speed()const{return speed;} void set_year(int y){year=y;} int get_year()const{return year;} const Cdrom & Sravnenie(const Cdrom &p) const
{return (speed<p.speed) ? p : *this; }

}; |
|
int main() |
|
{ |
|
Cdrom acer(52,2008); |
Cdrom nec(32,2001); |
Cdrom maxi=acer.Sravnenie(nec);
cout<<"\nCd-Rom maxi "<<maxi.get_speed()<<'\t'<<maxi.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &maxi= "<<&maxi;
return 0;
}
Использовать ссылку как в более раннем примере не удастся. С помощью приведения типов можно исправить ситуацию, но адреса у объектов будут различными (так как будет создаваться ещѐ один объект):
Cdrom &maxi=(Cdrom)acer.Sravnenie(nec);
или, что то же самое, но с явным указанием того, что вызывается конструктор
Cdrom &maxi=Cdrom(acer.Sravnenie(nec));
Пример. Строка acer.Sravnenie(nec)=sony; вызовет конструктор копирования, адреса у трех участвующих в операции объектов будут разными. Какой объект (acer или nec) получит значения объекта sony зависит от того, что вернѐт функция
Sravnenie().
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s; year=y; cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}
int get_speed()const{return speed;}

void set_year(int y){year=y;}
int get_year()const{return year;}
Cdrom & Sravnenie(Cdrom &p){return (speed<p.speed) ? p : *this; }
}; |
|
int main() |
|
{ |
|
Cdrom acer(52,2004); |
Cdrom nec(32,2001); |
Cdrom & maxi=acer.Sravnenie(nec);
cout<<"\nCd-Rom maxi "<<maxi.get_speed()<<'\t'<<maxi.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &maxi= "<<&maxi;
Cdrom sony(24,1998); acer.Sravnenie(nec)=sony;
cout<<"\nCd-Rom acer "<<acer.get_speed()<<'\t'<<acer.get_year()<<endl; cout<<"\nCd-Rom nec "<<nec.get_speed()<<'\t'<<nec.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &sony= "<<&sony;
return 0;
}
Замечание. Если же записать const, то оператор acer.Sravnenie(nec)=sony; приведѐт к ошибке.
#include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s; year=y; cout<<"\nConstr-1";}
Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;}
int get_speed()const{return speed;} void set_year(int y){year=y;}
int get_year()const{return year;}
const Cdrom & Sravnenie(const Cdrom &p) const
{return (speed<p.speed) ? p : *this; }
}; |
|
int main() |
|
{ Cdrom acer(52,2004); |
Cdrom nec(32,2001); |
Cdrom maxi=acer.Sravnenie(nec);
cout<<"\nCd-Rom maxi "<<maxi.get_speed()<<'\t'<<maxi.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &maxi= "<<&maxi;
Cdrom sony(24,1998); acer.Sravnenie(nec)=sony; // теперь ОШИБКА
cout<<"\nCd-Rom acer "<<acer.get_speed()<<'\t'<<acer.get_year()<<endl; cout<<"\nCd-Rom nec "<<nec.get_speed()<<'\t'<<nec.get_year()<<endl; cout<<"&acer= "<<&acer<<" &nec= "<<&nec<<" &sony= "<<&sony;

return 0;
}
Пример вспомогательный. Максимальный элемент массива.
// Maxelemmass.cpp:
//
#include "stdafx.h" #include <iostream> using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
const int n=10;
int x[n]={1,23,3,45,5,6,7,8,9,10}; int i;
printf("\n");
for (i=0;i<n;i++) printf(" %d ",x[i]);
//
for (int mx=x[0],i=1;i<n;i++) mx=(mx<x[i])?x[i]:mx; printf("\n Max object %d \n",mx);
return 0;
}
Пример. Находим максимальный (в каком-то смысле) объект – в данном примере объект класса Cdrom с наибольшим значением speed.
// maxobject.cpp : #include "stdafx.h" #include <iostream> using namespace std; class Cdrom{
int speed; int year; public:
Cdrom(int s,int y=2002){speed=s; year=y; cout<<"\nConstr-1";} Cdrom(){speed=32;year=1999;cout<<"\nConstr-2";}
Cdrom(const Cdrom & c){speed=c.speed;year=c.year;cout<<"\nConstr-3";} ~Cdrom(){cout<<"\nDestructor";}
void set_speed(int s){speed=s;} int get_speed()const {return speed;}
void set_year(int y){year=y;}
int get_year()const{return year;}
const Cdrom & Sravnenie(const Cdrom &p) const {return (speed<p.speed) ? p : *this; }
};
int _tmain(int argc, _TCHAR* argv[])
{
const int n=10;
Cdrom x[n]={Cdrom(52,2005),Cdrom(42,2004),Cdrom(54,2005), Cdrom(40,2003),Cdrom(58,2007),Cdrom(52,2004),Cdrom(62,2008), Cdrom(20,2002),Cdrom(16,2000),Cdrom(12,1999)};
int i;
Cdrom mx=x[0]; printf("\n");
for (i=0;i<n;i++) printf(" %d ",x[i].get_speed());
//
for (i=1;i<n;i++) mx=mx.Sravnenie(x[i]); printf("\n Max object %d \n",mx.get_speed());

return 0;
}