The answer is simple: when this was added to C++, references didn’t exist yet. If this were added to the C++ language today, it would undoubtedly be a reference instead of a pointer. In other more modern C++-like languages, such as Java and C#, this is implemented as a reference.
struct Date{ int year {}; int month {}; int day {}; void incrementDay(){ ++day;}};int main(){ const Date today {2020, 10, 14}; // const today.day += 1; // compile error: can't modify member of const object today.incrementDay(); // compile error: can't call member function that modifies member of const object return 0;}
#include <iostream>struct Date{ int year {}; int month {}; int day {}; void incrementDay()const // made const{ ++day; // compile error: const function can't modify member} void print()const // now a const member function{ std::cout << year << '/'<< month << '/'<< day;}};int main(){ const Date today {2020, 10, 14}; // const today.incrementDay(); today.print(); // ok: const object can call const member function return 0;}
Warning
构造函数不能被设置为常函数,因为它必定修改成员变量。
Tip
A member function that does not (and will not ever) modify the state of the object should be made const, so that it can be called on both const and non-const objects. Once a member function is made const, that function can be called on const objects. Later removal of const on a member function will break any code that calls that member function on a const object.
#include <iostream>struct Date{ int year {}; int month {}; int day {}; void print()const // const{ std::cout << year << '/'<< month << '/'<< day;}};int main(){ Date today {2020, 10, 14}; // non-const today.print(); // ok: can call const member function on non-const object return 0;}
如果部分成员变量确实需要常函数来修改,那么可以给成员变量加上一个 mutable 属性:
1 2 3 4 5 6
class A{public: mutable int test{}; void setInt(int a)const{ test = a; }}
Tip
Mutable allows the data member to be modified, even if the object containing the data member is const.
#include <iostream>class Accumulator{private: int m_value {0};public: void add(int value){ m_value += value; } // Here is the friend declaration that makes non-member function void print(const Accumulator& accumulator) a friend of Accumulator friend void print(const Accumulator& accumulator);};void print(const Accumulator& accumulator){ // Because print() is a friend of Accumulator // it can access the private members of Accumulator std::cout << accumulator.m_value;}int main(){ Accumulator acc{}; acc.add(5); // add 5 to the accumulator print(acc); // call the print() non-member function return 0;}
#include <iostream>class Accumulator{private: int m_value {0};public: void add(int value){ m_value += value; } // Friend functions defined inside a class are non-member functions friend void print(const Accumulator& accumulator){ // Because print() is a friend of Accumulator // it can access the private members of Accumulator std::cout << accumulator.m_value;}};int main(){ Accumulator acc{}; acc.add(5); // add 5 to the accumulator print(acc); // call the print() non-member function return 0;}
print 在类 Accumulator 内定义,但由于被声明为友元,因此它实际被视为非成员函数,无法调用 this 指针。
#include <iostream>class Storage; // forward declaration for class Storageclass Display{private: bool m_displayIntFirst {};public: Display(bool displayIntFirst) : m_displayIntFirst { displayIntFirst }{} void displayStorage(const Storage& storage); // forward declaration for Storage needed for reference here};class Storage // full definition of Storage class{private: int m_nValue {}; double m_dValue {};public: Storage(int nValue, double dValue) : m_nValue { nValue }, m_dValue { dValue }{} // Make the Display::displayStorage member function a friend of the Storage class // Requires seeing the full definition of class Display (as displayStorage is a member) friend void Display::displayStorage(const Storage& storage);};// Now we can define Display::displayStorage// Requires seeing the full definition of class Storage (as we access Storage members)void Display::displayStorage(const Storage& storage){ if(m_displayIntFirst) std::cout << storage.m_nValue<< ' '<< storage.m_dValue<< '\n'; else // display double first std::cout << storage.m_dValue<< ' '<< storage.m_nValue<< '\n';}int main(){ Storage storage {5, 6.7}; Display display {false}; display.displayStorage(storage); return 0;}
#include <iostream>class Cents{private: int m_cents{};public: Cents(int cents) : m_cents{ cents }{} int getCents()const{return m_cents; }};// note: this function is not a member function nor a friend function!Cents operator+(const Cents& c1, const Cents& c2){ // use the Cents constructor and operator+(int, int) // we don't need direct access to private members here return Cents{c1.getCents()+ c2.getCents()};}int main(){ Cents cents1{6}; Cents cents2{8}; Cents centsSum{ cents1 + cents2 }; std::cout << "I have "<< centsSum.getCents()<< " cents.\n"; return 0;}
#include <iostream>class Cents{private: int m_cents{};public: Cents(int cents) : m_cents{ cents }{} // add Cents + Cents using a friend function friend Cents operator+(const Cents& c1, const Cents& c2); int getCents()const{return m_cents; }};// note: this function is not a member function!Cents operator+(const Cents& c1, const Cents& c2){ // use the Cents constructor and operator+(int, int) // we can access m_cents directly because this is a friend function return{c1.m_cents + c2.m_cents};}int main(){ Cents cents1{6}; Cents cents2{8}; Cents centsSum{ cents1 + cents2 }; std::cout << "I have "<< centsSum.getCents()<< " cents.\n"; return 0;}
#include <iostream>class Cents{private: int m_cents {};public: Cents(int cents) : m_cents { cents }{} // Overload Cents + int Cents operator+(int value)const; int getCents()const{return m_cents; }};// note: this function is a member function!// the cents parameter in the friend version is now the implicit *this parameterCents Cents::operator+ (int value)const{ return Cents { m_cents + value };}int main(){ const Cents cents1 {6}; const Cents cents2 { cents1 + 2}; std::cout << "I have "<< cents2.getCents()<< " cents.\n"; return 0;}
7.2 流式输出运算符重载
C++ 已经重载了 <<(左移运算符)作为流插入运算符使用。<< 支持输出基本数据类型的数据,但有时我们想要输出规定之外的数据类型。
lass MyString{private: char* m_data{}; int m_length{};public: MyString(const char* source = ""){ assert(source); // make sure source isn't a null string // Find the length of the string // Plus one character for a terminator m_length = std::strlen(source)+ 1; // Allocate a buffer equal to this length m_data = new char[m_length]; // Copy the parameter string into our internal buffer for(int i{0}; i < m_length; ++i) m_data[i]= source[i];} void deepCopy(const MyString& source); MyString& operator=(const MyString& source); ~MyString() // destructor{ // We need to deallocate our string delete[] m_data;} char* getString(){return m_data; } int getLength(){return m_length; }};// assumes m_data is initializedvoid MyString::deepCopy(const MyString& source){ // first we need to deallocate any value that this string is holding! delete[] m_data; // because m_length is not a pointer, we can shallow copy it m_length = source.m_length; // m_data is a pointer, so we need to deep copy it if it is non-null if(source.m_data){ // allocate memory for our copy m_data = new char[m_length]; // do the copy for(int i{0}; i < m_length; ++i) m_data[i]= source.m_data[i];} else m_data = nullptr;}// Assignment operatorMyString& MyString::operator=(const MyString& source){ // check for self-assignment if(this != &source){ // now do the deep copy deepCopy(source);} return *this;}
Note
上面的例子使用深拷贝。和构造函数一样,拷贝赋值运算符也有自己的默认版本。
对于 C++ 默认使用的浅拷贝,可以参见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Possible implementation of implicit assignment operatorFraction& operator= (const Fraction& fraction){ // self-assignment guard if(this == &fraction) return *this; // do the copy m_numerator = fraction.m_numerator; m_denominator = fraction.m_denominator; // return the existing object so we can chain this operator return *this;}