Operatori de conversie de tipuri
C++ permite definirea funcției operatorului de conversie din tipul clasei curente într-un alt tip. Tipul în care se face conversia poate fi un tip fundamental sau un tip de clasă. În general, operatorul de conversie are următoarea formă:
class MyClass
{
public:
operator OtherType() const; // conversie din tipul MyClass în tipul OtherType
..........................
};
OtherType reprezintă tipul în care convertim obiectul. Tipul de returnare al funcției operatorului nu este indicat, deoarece tipul țintă este deja prezent în numele funcției, de aceea funcția trebuie să returneze un obiect de tipul OtherType.
Spre deosebire de majoritatea operatorilor, operatorii de conversie trebuie să fie definiți doar ca funcții-membru ale clasei. Nu pot fi definiți ca funcții obișnuite. De asemenea, aceștia sunt singurii operatori în care cuvântul cheie operator nu este precedat de tipul valorii returnate (în schimb, tipul returnat apare după cuvântul operator). Să luăm un exemplu simplu:
#include <iostream>
class Counter
{
public:
Counter(int number)
{
value = number;
}
operator int() const { return value; }
private:
int value;
};
int main()
{
Counter counter{25};
int n = counter; // conversie din Counter în int
std::cout << n << std::endl; // 25
// sau așa
int m {counter};
std::cout << m << std::endl; // 25
}
Aici, în clasa Counter este definit operatorul de conversie în tipul int:
operator int() const { return value; }
În acest caz returnăm pur și simplu valoarea variabilei value.
După aceasta putem, de exemplu, să atribuim unei variabile sau unui parametru de tip int o valoare de tip Counter – iar o astfel de valoare va fi convertită automat în int:
Counter counter{25};
int n = counter; // conversie din Counter în int - n=25
Datorită operatorului de conversie, o astfel de conversie de tip se efectuează implicit. Dar conversia tipului se poate face și explicit, de exemplu cu ajutorul funcției static_cast:
Counter counter{25};
int n = static_cast<int>(counter); // conversie explicită din Counter în int
std::cout << n << std::endl; // 25
// sau așa
int m {static_cast<int>(counter)}; // conversie explicită din Counter în int
std::cout << m << std::endl; // 25
Un alt exemplu – conversie în tipul bool:
#include <iostream>
class Counter
{
public:
Counter(double n)
{
value = n;
}
void print() const
{
std::cout << "value: " << value << std::endl;
}
// Operator de conversie în bool
operator bool() const { return value != 0; }
private:
int value;
};
// testăm operatorii
void testCounter(const Counter& counter)
{
counter.print();
if (counter)
std::cout << "Counter is non-zero." << std::endl;
if (!counter)
std::cout << "Counter is zero." << std::endl;
}
int main()
{
Counter counter1{22};
testCounter(counter1);
Counter counter2{0};
testCounter(counter2);
}
În acest caz, în operatorul de conversie, dacă valoarea value a obiectului Counter este egală cu 0, returnăm false, altfel returnăm true:
operator bool() const { return value != 0; }
Datorită acestui lucru putem folosi obiectul Counter în expresii condiționale, la fel ca și un tip bool:
if (counter)
if (!counter)
Este de remarcat că, deși nu am definit explicit operatorul ! (operatorul de negație logică) pentru tipul Counter, expresia !counter va funcționa cu succes, deoarece în acest caz obiectul counter va fi convertit implicit în bool.
Conversii explicite
Conversiile implicite nu sunt întotdeauna dorite. În acest caz ele pot fi dezactivate, definind funcția operatorului cu ajutorul cuvântului cheie explicit:
#include <iostream>
class Counter
{
public:
Counter(int number)
{
value = number;
}
explicit operator int() const { return value; } // Doar conversii explicite
private:
int value;
};
int main()
{
Counter counter{25};
int n = static_cast<int>(counter); // conversie explicită
std::cout << n << std::endl; // 25
// int m = counter; // nu este permis, doar conversii explicite
// std::cout << m << std::endl;
}
Conversie între clase
În mod similar, se pot face conversii între tipuri de clase:
#include <iostream>
class PrintBook;
// carte electronică
class Ebook
{
public:
Ebook(std::string book_title)
{
title=book_title;
}
operator PrintBook() const;
std::string getTitle(){return title;}
private:
std::string title;
};
// carte tipărită
class PrintBook
{
public:
PrintBook(std::string book_title)
{
title=book_title;
}
operator Ebook() const;
std::string getTitle(){return title;}
private:
std::string title;
};
Ebook::operator PrintBook() const
{
return PrintBook{title};
}
PrintBook::operator Ebook() const
{
return Ebook{title};
}
int main()
{
PrintBook book{"C++"};
Ebook ebook{ book }; // digitalizăm cartea – din PrintBook în Ebook
std::cout << ebook.getTitle() << std::endl; // C++
PrintBook print_book{ebook}; // tipărim cartea din Ebook în PrintBook
std::cout << print_book.getTitle() << std::endl; // C++
}
Aici clasa Ebook reprezintă o carte electronică, iar PrintBook – o carte tipărită. Cu ajutorul operatorilor de conversie putem converti un obiect dintr-un tip în altul și invers, adică, metaforic vorbind, să digitalizăm sau să tipărim o carte. Pentru a evita referințele circulare ale unei clase către cealaltă, implementarea operatorilor de conversie este separată de declarație.