11/8/2007 Nesneye Yonelik Programlama 8.1
Opertörlerin Aşırı Yüklenmesi Operator Overloading
• Operatörün aşırı yüklenmesi; bir operatöre standart anlamı dışında yeni anlamlar kazandırmak anlamına gelir.
• Aşırı yüklenmiş operatör giriş olarak aldığı operandın (parametrelerin ) tipine göre anlam kazanır:
Örnek: standard matematikte, + ve – integers, reals, vectors, complex numbers, matrices, vb için tanımlanır.
C++ da, << operatorü aşırı yüklenmiştir.
1. Anlamı: akış çıkış operatörü - stream insertion operator 2. Anlamı: biteşlem sola kaydırma operatörü - Bitwise left-shift
operator
11/8/2007 Nesneye Yonelik Programlama 8.2
Ne zaman operatör aşırı yükleme yapılır
• Operatör aşırı yüklenmesi fonksiyon çağrılması ve üye fonksiyon çağırmaların notasyonunu kolaylaştırır:
Örnek: Color3 = mix (Color1, Color2); // yerine Color3= Color1 + Color2; // yazılabilir
burada
“+” colorlar için tanımlanmıştır.
• Operatör aşırı yüklenmesi aynı zamanda kullanıcı tarafından tanımlanmış yeni tiplerle hali hazırda var olan tipler arasındaki benzerliği pekiştirmek için kullanılırlar
Örnek: << ve >> operatörleri kullanıcı tanımlı tipler için çıktı/girdi işlemleri için tanımlanabilir.
Aşırı Yüklenebilen C++ Operatörleri
• Assignment operator ve stream operators
=, <<, >>
• Arithmetic operators
+, -, *, /, %, +=, -=, *=, /=, %=
• Increment and decrement operators
++, --• Logical operators
!, &&, ||, <<, >>
• Bitwise logical operators
~, ^, &, |, ^=, &=, |=
• Relational and equality operators
<, >, <=, >=, ==, !=
• Dynamic memory allocation operators
new, delete, new [ ], delete [ ]• Other operators
( ), [ ], ->, the comma operator ( , )
Aşırı Yüklenemeyen Operatörler
• . Dot member access operator
• .* Dot member access using pointer
• :: scope resolution operator
• ?: ternary conditional operator
• sizeof
11/8/2007 Nesneye Yonelik Programlama 8.5
C++ da operatörler nasıl aşırı yüklenir.
Yazım:
<return_type> <class_name>::operator#(<parameters>){
<function body that defines operator “#”>
} Anlamı:
• Operator # kullanıcının tanımladığı sınıfların (ismi class_name) nesneleri için yeniden tanımlanır.
• <parameters> tipik olarak <class_name> tipinde bir giris parametresi ve <return_type> tipik olarak <class_name> tipinde geri dönen değerin tipini gösterir.
11/8/2007 Nesneye Yonelik Programlama 8.6
Örnek
// Contents of the header file “point.h”
class Point { public:
// define a default constructor Point (int i=0, int j=0, int k=0 ) {
setPoint (i,j,k);
}
Point operator+ ( Point p) ; Point operator= (Point p);
void displayPoint () const;
void setPoint (int, int, int);
private:
int x,y,z; // 3-D coordinates };
// start of file “point.cpp”
#include <iostream>
#include “point.h”
void Point::setPoint (int i, int j, int k) { x = i;
y = j;
z = k;
}
void Point::displayPoint ( Point p) const { cout << “(“ << x
<< “, “ << y
<< “, “ << z
<< “)”;
}
Point Point::operator+ (Point p) { Point result;
result.x = x + p.x;
result.y = y + p.y;
result.z = z + p.z;
return result;
}
Point Point::operator= (Point p) { x = p.x;
y = p.y;
z = p.z;
return *this;
}
11/8/2007 Nesneye Yonelik Programlama 8.9
#include “point.h”
int main ( ) {
Point a (3,2,1), b(13,14,15), c;
a.displayPoint ( );
b.displayPoint ( );
c = a + b;
c.displayPoint ( );
c = a + b + c;
c.displayPoint ( );
c = b = a;
c.displayPoint ( );
b.displayPoint ( );
return 0;
}
11/8/2007 Nesneye Yonelik Programlama 8.10Operatör aşırı yüklemede kısıtlamalar
• Operatör yüklemeyle operatörlere hali hazırda var olan tiplerdeki davranışlarıdan farklı bir davranış
kazandırılamaz.
• Operatör en az bir tane kullanıcı tanımlı tip veya sınıfı argüman olarak almalıdır.
• Operatör aşırı yüklemesi kapalı şekilde yapılamaz:
# operatörünü aşırı yüklemek # = operatörünün de aşırı yüklemek anlamına gelmez.
== operatörünü aşırı yüklemek != operatörünün de aşırı yüklemek anlamına gelmez.
Hint: ilgili bir kaç operatörü aşırı yüklemek istersek, her birini açık bir şeklide ayrı ayrı aşırı yüklemek gerekir.
• Aşırı yükleme parametre sayısını değiştiremez.
11/8/2007 Nesneye Yonelik Programlama 8.13 11/8/2007 Nesneye Yonelik Programlama 8.14
C++ << ve >> ye nasıl davranır?
• C++ derleyicisi cout << phone ile karşılaştığında, bunu fonksiyon çağrımı
operator << (ostream &, const PhoneNumber &) yazılışına dönüştürür.
• Sonuç sonraki çıktı argümanları kabul eden ostream dir ( << left associative).
• C++ derleyicisi cin >> phone ile karşılaştığında, bunu fonksiyon çağrımı
operator>>(istream &, PhoneNumber &) yazılışına dönüştürür.
• Sonuç daha sonraki girdi argümanlarını kabul eden istream dir ( >> left
associative ).
11/8/2007 Nesneye Yonelik Programlama 8.17
• Complex sınıfı için extraction operatörünün (>>) aşırı yüklü olduğunu varsayalım:
istream &operator >> (istream &input, Complex &cnum) {
input >> cnum.real >> cnum.imag;
return input;
}
• Aşırı yüklemiş operatör şu şekilde kullanılabilir:
Complex c_obj;
cin >> c_obj;
İkinci ifade cin >> c_obj; aşağıdaki ifadeye eşittir.
operator>> (cin, c_obj );
ve çalıştırıldığında ise
cin >> c_obj.real >> c_obj.imag;
ifadesine eşittir.
11/8/2007 Nesneye Yonelik Programlama 8.18
• Her iki parametre de referansla iletilir:
– Sistem stream lar hakkında bir takım bilgileri update ettiğinden dolayı, stream nesneleri daima reference ile fonksiyona iletilir veya geçirilir.
– İkinci nesnenin (Complex object) veri üyeleri update edildiğinden fonksiyona referansla geçirilir.
• Stream referans olarak geri döndürülür. Böylece input (output) operatörlerini ardı sıra tekrarlı olarak kullanabiliriz (cascade).
Complex c1_obj, c2_obj;
cin >> c1_obj >> c2_obj;
• Single call (tekli öçağrım) olarak kullanıldığında:
cin >> c_obj;
Döndürülen değer kullanılmaz. İkinci arguman c_obj birinci argümana göre değiştirilir.
• Zincirli bir şekilde çağrım yapıldığında:
cin >> c1_obj >> c2_obj;
İkinci argüman c1_obj birinci argüman cin e göre değiştirilir ve döndürülen değer ikinci fonksiyon çağrımı için ilk argüman görevini görür
operator >> (input, c2_obj );
Buradaki input c1_obj nin değerini içeren istream nesnesidir.
Unary Operatör Aşırı Yüklemesi
• Tek argümanlı (Unary) operatörlerin aşırı yüklemesinin iki yolu vardır:
– Argümansız non-static member fonksiyonu kullanarak (nesne argüman olarak davranır)
Örnek:
class String { public:
bool operator!() const;
...
};
– Bir argümanlı non-member fonksiyonu kullanarak (argüman nesne veya nesneye referana olmalıdır)
Örnek:
class String {
friend bool operator! (const String &) ...
11/8/2007 Nesneye Yonelik Programlama 8.21
İkili Operatörlerin Aşırı Yüklenmesi
• İkili (binary) operatörlerin aşırı yüklenmesi için iki yöntem vardır:
– Bir arguümanlı non-static üye fonksiyon kullanarak (nesne ikinci argüman olarak davranır)
class String { public:
const String &operator+= ( const String & );
...
};
– İki argümanlı bir üye olmayan fonksiyon kullanarak (argümanlardan bir tanesi nesne veya nesneye referans olmalıdır).
class String {
friend const String &operator+=(String &, const String &) ...
};
11/8/2007 Nesneye Yonelik Programlama 8.22
Kopya Yapıcı ve Atama Operatörleri Copy Constructors & Assignment Operators
• Varsayılan olarak, bir nesne ataması yapıldığında veya nesnenin kopyası gerektiğinde, karşılıklı elemanlar arası kopyalama (memberwise copy) kullanılarak kopyalama işlemi gerçekleştirilir.
• Eğer nesneler dinamik olarak tahsis edilen bellek alanlarına sahip ve kopya yapıcısı yoksa hafıza ile ilgili problemler ortaya çıkar:
– Bir kopyayı yok etmek tahsis edilen hafızanın silinmesi anlamına gelir ve diğer nesneler tarafından işaret edilen hafıza alanının da yok edilmesi anlamına gelir.
• Çözüm: dinamik olarak tahsis edilen nesnelerinin
kopyasını oluşturmak için bir kopya yapıcı ve bir aşırı
yüklenmiş atama operatörü tanımlamaktır.
11/8/2007 Nesneye Yonelik Programlama 8.25
Copy Constructors
• Sınıf deklerasyonunda,
<class_name> (const <class_name> &) eklenmelidir.
• Yeni üye fonksiyonun tanımlanması:
– (1) dinamik olarak var edilen bütün alt nesneler için gerekli hafıza tahsis edilmelidir.
– (2) verilen nesnenin alt nesnelerinin içeriği kopya nesnelere verilmelidir.
11/8/2007 Nesneye Yonelik Programlama 8.26
Copy Constructor
• sınıf için kopya yapıcı tanımlandığında, kopya yapıcı otomatik olarak aşağıdaki durumlarda çağrılır
– Bir nesne fonksiyona değerle iletildiğinde – Bir fonksiyondan bir nesne döndürüldüğünde – Var olan bir nesnenin kopyası olarak yeni bir
nesneye başlangıç ataması yapıldığında.
Atama Operatörünün Aşırı Yüklenmesi
• Sınıf deklarasyonunda
const <class> &operator= (const <class> &);
• Diğer ikili operatörlerin aşırı yüklenmesinde olduğu gibi, derleyiciye şu kod örneğini
object1 = object2;
aşağıdaki fonksiyon çağrılmasıyla yer değiştirir:
object1.operator= (object2);
• Atama operatörü fonksiyon tanımında
– Kendi kendisine atama yapmaya izin verilmemelidir.
– Zincirleme atamalara izin vermek için nesne bir işaretçi geri döndür ( return *this)
(örnek: object1 = object2 = object3; )
built-in tipler arasında dönüştürme
• C++ derleyicileri operatörler farkliı tipe sahip argümanlara uygulandığında güvenli tipe çevrimleri için otomatik olarak kod yerleştirir (promotion veya implicit conversion)
Örnek: int + double double sonuç üretir.
• Bazen programcı explicit (açık) tip dönüştürmeye ihtiyac duyar
Örnek: avg = static_cast <float> (sum) / num;
Yazım: static_cast < <typeName> > ( <argument>)
11/8/2007 Nesneye Yonelik Programlama 8.29
Kullanıcı tanımlı tipler arasında dönüştürmeye
• Bazen kullanıcı tanımlı tipler arasında dönüştürmeye ihtiyaç duyabiliriz.
• Bunun için programcı dönüşüm fonksiyonları tanımlamalıdır:
• Dönüştürme Yapıcıları - Conversion constructors:
– Tek argüman alan ve argüman tipini (built-in type veya user-defined type) nesne tipine çeviren yapıcı.
• Dönüştürme operatöerleri - Conversion ( cast ) operators:
– Non-static üye fonksiyonları kullanarak bir sınıfa ait nesneyi
• başka bir sınıfın nesnesine dönüştürür.
• hali hazırda var olan tiplerdeki nesneye dönüştürür.
11/8/2007 Nesneye Yonelik Programlama 8.30
Dönüştürme Operatör Fonksiyonları
• Dönüştürme operatör deklarasyonu yazımı:
<old_class>::operator<new_class> const;
• Anlamı:
Giriş parametresi <old_class> tipinden
<new_class> sınıfının bir nesnesine dönüştürür – Geri dönüş tipi gerekmez
– Dönüştürme operator şöyle çağrılır:
(newClass ) objectName ;
Aşırı yüklenmiş dönüştürme operatör fonksiyonları overloaded cast operator functions
• İki veya daha fazla dönüştürme operatörü aşırı yüklenmek suretiyle sınıfın nesnesi iki veya daha fazla kullanıcı tanımlı sınıfların nesnelerine dönüştürülür
class A { public:
operator int( ) const;
operator double ( ) const;
operator B ( ) const;
… private:
… };
Dönüştürme operatörlerini kullanarak kapalı dönüştürme
• Bir dizi dönüştürme fonksiyonu tanımlandığı zaman derleyici bunları kullanarak kapalı tip dönüştürme (implicit type conversion) yapar.
• Örnek: Kullanıcı tarafından bir dönüştürme operatörü tanımlandığını ve bunun A sınıfının nesnelerini char * ya çevirdiğini varsayalım:
• Derleyici operatör char * otomatik olarak çağırmak suretiyle aşağıdaki kodun doğru sonuç üretmesini sağlayacak
cout << objectName; // objectName A sınıfının nesnesi
11/8/2007 Nesneye Yonelik Programlama 8.33
Overloading ++ and --
• Önek (prefix) operatörleri (önceden artırma ve önceden azaltma) diğer tekli önek (prefix unary) operatörler gibi düşünülerek aşırı yüklenebilirler.
• Sonek (postfix) operatörler (sonradan artırma ve sonradan azaltma) operatörlere 0 değerli bir sahte giriş parametresi verilmek suretiyle önekli
versiyonlarından ayırt edilirler.
11/8/2007 Nesneye Yonelik Programlama 8.34
Önceden artırma operatörünü aşırı yükleme
• Method 1: üye fonksiyon kullanarak – s nin A sınıfınin nesnesi olduğunu varsayalım – Derleyici ++s yi gördüğünde
– s.operator++( ) yi üretir.
– Dolayısıyla A sınıfı, A &operator++( ) üye fonksiyonuna sahip olmalıdır.
– Fonksiyon tanımlaması A &A::operator++( )
• Method 2: üye olmayan friend fonksiyonu kullanarak – friend A &operator++(A &) // decleration – A &operator++ (A &) // definition
Sonradan artırma operatörünü aşırı yükleme
• Method 1: üye fonksiyonu kullanarak – s nin A sınıfının nesnesi olduğunu varsayalım – Derleyici s++ yı gördüğünde
– s.operator++(0) yi üretir.
– Dolayısıyla A sınıfı, A &operator++(int) üye fonksiyonuna sahip olmalıdır.
– Fonksiyon tanımlama A &A::operator++(int)
• Method 2: üye olmayan friend fonksiyonu kullanarak – friend A &operator++ (A &, int) // decleration – A &operator++ (A &, int) // definition
#include <iostream.h>
#include <stdlib.h>
struct CELL{
int element;
struct CELL *next;
};
class List{
public:
List( );
List(const List &);
~List( );
void Add_element(int);
void Remove_element(int);
void print( );
const List& operator=(const List &);
const List& operator++();
const List operator++(int);
void sort( );
private:
struct CELL *list_header;
struct CELL* create_element(int);
struct CELL* insert_sorted_list(struct CELL*,struct CELL*);
11/8/2007 Nesneye Yonelik Programlama 8.37 struct CELL* List::create_element(int i) {
struct CELL *ptr;
ptr = (struct CELL *) malloc(sizeof(struct CELL));
ptr->element = i;
return ptr;
}
11/8/2007 Nesneye Yonelik Programlama 8.38
List::List( ) {
cout<<"birinci List constructor cagrildi\n";
list_header = 0;
}
List::List(const List &list){ struct CELL *temp1, *temp2;
cout<<"ikinci List constructor cagrildi\n";
list_header = 0;
if(list.list_header == 0) return;
temp2=list.list_header;
temp1 = list_header =
create_element(temp2->element);
temp2 = temp2->next;
while(temp2) {
temp1->next = create_element(temp2-
>element);
temp2 = temp2->next;
temp1 = temp1->next;
}
temp1->next = 0;
}
List::~List( ) { struct CELL *temp;
cout << “List destructor cagrildi\n";
temp= list_header;
while(temp) {
list_header = list_header->next;
delete temp;
temp=list_header;
} }
const List& List::operator=(const List & input_list){ struct CELL *header1 = list_header;
struct CELL *header2 = input_list.list_header, *temp = 0;
if(header2==0) list_header = 0;
while (header1 && header2) {
temp = header1; header1->element = header2->element;
header1 = header1->next; header2 = header2->next;
} // while if(header2 == 0) {
if (temp) temp->next = 0;
temp = header1;
while (temp) {
header1 = header1->next;
delete temp; temp = header1;
} // while } // if (header == 0) else {
if(header2 && !list_header) {
list_header = temp = create_element(header2->element);
header2=header2->next;
}
while(header2) {
temp->next = create_element(header2->element);
temp = temp->next; header2 = header2->next;
} // while
if(temp) temp->next = 0;
} // else return *this;
11/8/2007 Nesneye Yonelik Programlama 8.41 const List& List::operator ++() {
struct CELL *temp = list_header;
while(temp) { temp->element++;
temp = temp->next;
}
return *this;
}
const List List::operator ++(int) { struct CELL *temp_header = list_header;
List temp = *this;
while(temp_header) { temp_header->element++;
temp_header = temp_header->next;
}
return temp;
}
11/8/2007 Nesneye Yonelik Programlama 8.42
void List::Add_element(int i) {
struct CELL *new_element_ptr = create_element(i);
new_element_ptr->next = list_header;
list_header = new_element_ptr;
}
void List::Remove_element(int i) { struct CELL *temp1,*temp2;
temp1=temp2=list_header;
while(temp2) {
if(temp2->element == i) {
if(temp1==temp2) list_header = temp2->next;
else temp1->next=temp2->next;
free(temp2);
break;
}
temp1=temp2;
temp2=temp2->next;
} }
void List::print( ) {
struct CELL *temp_ptr = list_header;
cout<<endl;
while(temp_ptr) {
cout << temp_ptr->element <<“ “;
temp_ptr=temp_ptr->next;
}
cout <<endl;
}
struct CELL* List::insert_sorted_list(struct CELL* header, struct CELL* cell) { if(!header) {
cell->next = 0;
return cell;
}
else if(cell->element <= header->element) { cell->next = header;
return cell;
} else {
header->next = insert_sorted_list(header->next,cell);
return header;
} }
void List::sort( ) {
struct CELL *temp1,*temp2;
temp1 = list_header;
list_header = 0;
while (temp1) { temp2=temp1->next;
list_header = insert_sorted_list(list_header,temp1);
temp1 = temp2;
11/8/2007 Nesneye Yonelik Programlama 8.45 class Stack{
public:
Stack( ); // default constructor Stack(Stack &); // copy constructor
~Stack( ); // destructor void push(int);
bool pop(int &);
void print( );
operator List( ) const; // overloading cost operator
operator List*( ) const; // overloading cost operator
private:
struct CELL *stack_ptr;
};
11/8/2007 Nesneye Yonelik Programlama 8.46
Stack::Stack( ) {
cout << "birinci Stack constructor cagrildi\n";
stack_ptr = 0;
}
Stack::Stack(Stack & stack) { CELL *temp = stack.stack_ptr;
CELL *cell;
cout << “ikinci Stack constructor cagrildi\n";
stack_ptr = 0;
if (!temp) return;
stack_ptr = cell =
(struct CELL *) malloc(sizeof(struct CELL));
cell->element = temp->element;
temp = temp->next;
while(temp) { cell->next =
(struct CELL *) malloc(sizeof(struct CELL));
cell->next->element = temp->element;
cell = cell->next;
temp = temp->next;
}
cell->next = 0;
}
Stack::~Stack( ) { CELL *temp;
cout <<"Stack destructor cagrildi\n";
while(stack_ptr) {
temp = stack_ptr->next;
free(stack_ptr);
stack_ptr = temp;
} }
void Stack::push(int element) {
CELL *cell = (struct CELL *) malloc(sizeof(struct CELL));
cell->element = element;
cell->next = stack_ptr;
stack_ptr = cell;
}
bool Stack::pop(int &element) { if(!stack_ptr) return false;
else {
CELL *cell = stack_ptr->next;
element = stack_ptr->element;
free(stack_ptr);
stack_ptr = cell;
return true;
} }
void Stack::print( ) { CELL *cell = stack_ptr;
cout << endl;
while(cell) {
cout << cell->element <<" ";
cell = cell->next;
}
cout << endl;
}
11/8/2007 Nesneye Yonelik Programlama 8.49 Stack::operator List( ) const {
List list;
CELL *cell = stack_ptr;
while(cell) {
list.Add_element(cell->element);
cell = cell->next;
}
return list;
}
Stack::operator List*( ) const { List *list = new List;
CELL *cell = stack_ptr;
while(cell) {
list->Add_element(cell->element);
cell = cell->next;
}
return list;
}
11/8/2007 Nesneye Yonelik Programlama 8.50
int main( ) {
class List list1; // 1
list1.Add_element(10); // 2
list1.Add_element(20); // 3 list1.Add_element(40); list1.print(); // 4 class List list2; // 5
list2 = list1++; // 6
list1.print(); // 7
list2.print(); // 8
class Stack stack1; // 9 stack1.push(3000); stack1.push(5000); // 10 stack1.push(1000); stack1.push(4000); // 11
stack1.print( ); // 12
class Stack stack2 = stack1; // 13
int element; // 14
if (stack2.pop(element)) // 15 cout << element <<endl; // 16
stack2.print( ); // 17
list1 = (List) stack2; // 18 cout <<"---\n"; // 19
list1.print( ); // 20
class List *list_ptr; // 21 list_ptr = (List*)stack1; // 22 list_ptr->print( ); // 23
delete list_ptr; // 24
return 0; // 25
}