#include "UC.h" #include // It’s fine to have using namespace in a *.cc file. Nobody #includes a // *.cc file, so you’re not polluting anyone’s namespace. using namespace std; UC::UC() = default; // default implementation is fine UC::UC(const char *p) : data(p) { upify(); } UC::UC(const string& s) : data(s) { upify(); } UC::UC(const UC& u) = default; // no conversion needed; data already caps UC& UC::operator=(const UC& u) = default; UC::~UC() = default; UC& UC::operator+=(const UC& u) { data += u.data; // All data is already uppercase. return *this; } // Return the data member by const reference, not by value. // Returning by value would require copying the data. This is faster. const string& UC::str() const { return data; } const char *UC::c_str() const { return data.c_str(); } // Ensure that all letters in the string are uppercase. It’s expensive // if we appended only a few characters to the end of a long string. void UC::upify() { for (char& c : data) // Note that it’s a reference. if (islower(c)) // Avoid dirty cache, at least. c = toupper(c); } // Free functions start here. These functions can’t be methods, because // their left-hand operands aren’t UC instances. If they were methods, // they’d have to be methods of const char * (not a class) or // std::string and std::ostream (not our classes to modify). // // These functions do NOT have to be friends. To access the std::string // inside the UC, they use the public getter method .str(). // Here, operator+ is a non-member function, because it might have // a non-UC as the left operand, e.g.: // UC u("foo"); // cout << "xyz" + u; UC operator+(const UC& lhs, const UC& rhs) { // We return a UC, but the expression is std::string + std::string. // Fine: the result of std::string + std::string is std::string, // and that std::string will be implicitly converted to a UC by the // compiler calling the UC ctor that takes a std::string. return lhs.str()+rhs.str(); } std::ostream& operator<<(std::ostream& os, const UC& u) { return os << u.str(); }