[C++ Primer] C14 关于c++重载,你需要知道的一些知识

Chapter 14 Overloaded Operations and Conversions

basic concepts

类外重载

回顾top-level const 和 low-level const:

  • C++ Primer states: We use the term top-level const to indicate that the pointer itself is a const; When a pointer can point to a const object, we refer to that const as a low level const.
int x = 10;
int* const p1 = &x; // top level const
int* p2 = p1; 
int* 和 int* const不相等。然而,当一个带有const限定符的变量或表达式被赋值给一个没有const限定符的变量或表达式,而且它们的类型相同(或可以被转换成相同的类型),那么const限定符会被忽略。这在C++中被称为“顶层const”。
本例中,p1是指向常量的常量指针,我们可以将它赋值给一个指向整数的非常量指针p2,因为在这种情况下const限定符会被忽略

const int* p1 = &x; // loew-level const
int* p2 = p1;
本例中,p1是一个指向常量整数的指针,p2是一个指向非常量整数的指针,low level const不能将const int*转化为一个int*

类内和类外重载运算符:

class A{
public:
    int a_;
    A() :a_(0) {}
    A(int a) : a_(a) {}
    A& operator+(const A& a) {
        cout << "in operator +" << a.a_ << endl;
        this->a_ += a.a_;
        return *this;
    } // lhs default is this*, rhs is a
};

A operator+(const A& a, const A& b) {
    A res;
    res.a_ = a.a_ + b.a_;
    return res;
}

int main() {
    A a1(1);
    A a2(2);
    a2 = a1 + a2; // a2 = 3, a1 = 3. cause a1 = 3, then assign to a2. By default we use operator inside the class first. If we comment the operator overload in the class ->
    A a3;
    a3 = a1 + a2; // a3 = 3, a1 = 1, a2 = 2; 
}

不能重载的对象

Ordinarily, the comma, address-of, logical AND, and logical OR operators should not be overloaded.

输入输出重载

在上例中,如果我们用cout << a1 << endl; 会怎么样? -> 编译报错,因为cout不支持我们定义的类A,所以我们要在A类中重写cout

#include <ostream>
class A{
    friend ostream& operator<<(ostream&, const A&); // declare
public:
    int a_;
    A() :a_(0) {}
    A(int a) : a_(a) {}
    A& operator+(const A& a) {
        cout << "in operator +" << a.a_ << endl;
        this->a_ += a.a_;
        return *this;
    } // lhs default is this*, rhs is a
};

// define
ostream& operator<<(ostream& os, const A& a) {
    cout << "in operator <<" << endl;
    os << a.a_ << endl;
    return os;
}

int main() {
    A a(1);
    cout << a << endl;
}

in operator <<
1

IO Operators Must Be Nonmember Functions

    1. 为什么有lhs, rhs? operator+(this, const A& a) operator<<(ostream& os, const A& a):我们不能使用friend ostream& operator<<( const A&);的方式来定义友元函数,因为这样并没有提供一个输出流对象作为函数参数。在输出A类型对象时,我们需要将其写入到一个特定的输出流对象中。因此,我们需要在参数列表中显式地传递一个ostream对象的引用,以便在函数内部访问并写入数据到这个流对象中。 注意:ostream 不能复制
    1. const ostream& 为什么不行?返回一个const类型的ostream对象将禁止对该对象进行修改,这意味着我们不能向流中写入数据或更改其状态。而我们希望在输出时改变流的状态
    1. 为什么friend/为什么在类外实现输入输出流重载?对于成员函数来说,重载运算符必须作为类的一部分实现,这意味着它们只能访问类的公共成员和保护成员,而不能访问类的私有成员。因此,重载<<和>>运算符作为类的成员函数是不可行的

Arithmetic and Relational Operators overload

类内写会自动绑定lhs为this,类外就不会

重载等号:

int main() {
    map<int,int> m1 = {
        {1,2},
        {3,4}
    };
    map<int,int> m2 = {
        {1,2},
        {3,4}
    };
    cout << (m1 == m2) << endl; // 1 (长度,每一个元素)
}

states from book:

  • If a class has an operation to determine whether two objects are equal, it should define that function as operator== rather than as a named function
  • If a class defines operator==, that operator ordinarily should determine whether the given objects contain equivalent data
  • Ordinarily, the equality operator should be transitive, meaning that if a == b and b == c are both true, then a == c should also be true(== 具有传递性)
  • If a class defines operator==, it should also define operator!=. Users will expect that if they can use == then they can also use !=, and vice versa. (== 和 != 同时定义)
  • One of the equality or inequality operators should delegate the work to the other. (!= 和 == 之间其中一个的工作需要交给另外一个,即!= 中return !(m1 == m2);)
    

Assignment operator

a class can define additional assignment operators that allow other types as the right-hand operand

class A {
public:
    int a_;
    A() :a_(0) {}
    A& operator+(const A& a) {
        cout << "in operator +" << a.a_ << endl;
        this->a_ += a.a_;
        return *this;
    } // lhs default is this*, rhs is a

    vector<int> vec;
    //传入initializer_list的元素会被默认转换为const
    A& operator=(/*this*/std::initializer_list<int> list) {
        cout << "in operator=" << endl;
        for (const int &num : list) {
            vec.push_back(num); // copy assignment
        }
        return *this;
    }
};

int main() {
    A a;
    a = {1,2,3,4};
    cout << a.vec.size() << endl; // 4
}

Assignment operators can be overloaded. Assignment operators, regardless of parameter type, must be defined as member functions

subscript operator

Classes that represent containers from which elements can be retrieved by position often define the subscript operator, operator[]. Like vector[], map[1]

If a class has a subscript operator, it usually should define two versions: one that returns a plain reference and the other that is a const member and returns a reference to const.

class A {
public:
    int a_;
    A() :a_(0) {}
    A(int a) : a_(a) {}
    A& operator+(const A& a) {
        cout << "in operator +" << a.a_ << endl;
        this->a_ += a.a_;
        return *this;
    } // lhs default is this*, rhs is a

    vector<int> vec = {1,2};
    // To be compatible with the ordinary meaning of subscript, the subscript operator
    // usually returns a reference to the element that is fetched
    int& operator[](std::size_t index) {
        cout << "in non-const" << endl;
        return vec[index]; // copy
    }

    const int& operator[](std::size_t index) const{
        cout << "in const" << endl;
        return vec[index]; // non copy
    }

    A& operator=(std::initializer_list<int> list) {
        for (const int&num : list) {
            vec.push_back(num);
        }
        return *this;
    }
};

int main() {
    A a;
    a = {1,2,3};
    const A a2(2);

    cout << a[4] << endl;
    cout << a2.operator[](0) << endl;
    cout << a2[0] << endl;
    return 0;
}

in non-const
3
in const
1
in const
1

must be a member function

Increment and Decrement Operators (前缀加加和后缀加加)

  • better in member function
  • return reference when prefix++
  • differentiate prefix++ and postfix++: using int to distinguish between prefix and postfix

To be consistent with the built-in operators, the prefix operators should return a reference to the incremented or decremented object.

To be consistent with the built-in operators, the postfix operators should return the old (unincremented or undecremented) value. That value is returned as a value, not a reference.

class point{
public:
    int x_;
    int y_;
    point(int x, int y) : x_(x), y_(y) {}
    point& operator++() {
        // prefix
        x_ += 1;
        y_ += 1;

        cout << "in prefix " << x_ << " " << y_ << endl;
        return *this;
    }
    point& operator++(int) {
        // postfix
        x_ += 1;

        cout << "in postfix " << x_ << " " << y_ << endl;
        return *this;
    }
};


int main() {
    point p(1,2);
    ++p;
    p++;
}

in prefix 2 3
in postfix 3 3

Member Access Operators

dereference operator * and the arrow operator -> are used to access members of an object through a pointer:

  • Operator arrow must be a member. The dereference operator is not required to be a member but usually should be a member as well.

overload ->, we need to return a pointer to an object with member functions or variables that you want to access:

class MyClass{
public:
    int value;
    class InnerClass {
    public:
        string s = "I'm a member of InnerClass!";
        void foo() const {
            cout << "foo" << endl;
        }
    };
    InnerClass* operator->() const {
        return inner;
    }
    ~MyClass() {
        cout << "In destructor" << endl;
        delete inner;
    }
private:
    InnerClass* inner = new InnerClass();
};


int main() {
    MyClass a;
    a->foo();
    cout << a->s << endl;
    return 0;
}

foo
I'm a member of InnerClass!
In destructor

writing point->mem is equivalent to:
(*point).mem;
point.operator()->mem;

The overloaded arrow operator must return either a pointer to a class type or an object of a class type that defines its own operator arrow.

function call operator overload

int operator()(){
    return a_ + 1;
}

14.8.1. Lambdas Are Function Objects

#include <algorithm>
int main() {
    vector<int> a = {4,7,34,1,3,8,2};
    std::sort(a.begin(), a.end(), [](const int& i, const int& j) {return i < j;});
    for (int& num : a) {
        cout << num << endl;
    }
}

1
2
3
4
7
8
34

by default, the function-call operator in a class generated from a lambda is a const member function

class comparator{
public:
    // overload (), overload function call! 
    bool operator()(const int& i, const int& j) const {
        return i < j;
    }
};

#include <algorithm>
int main() {
    vector<int> a = {4,7,34,1,3,8,2};
    vector<int> b = {4,7,34,1,3,8,2};
    std::sort(a.begin(), a.end(), [](const int& i, const int& j) {return i < j;});
    for (int& num : a) {
        cout << num << endl;
    }
    std::sort(b.begin(), b.end(), comparator()); // why use comparator?
    // The syntax comparator() creates a temporary object of the comparator class by calling its default constructor. This temporary object is then passed to 
    // std::sort as the third argument.

    // we can also write:
    comparator MyComp;
    std::sort(b.begin(), b.end(), MyComp); // same!
    for (int& num : b) {
        cout << num << endl;
    }
}
1
2
3
4
7
8
34

1
2
3
4
7
8
34

When you create a lambda function, the compiler generates a unique functor type that corresponds to the lambda's signature(In this case, it's the class comparator). The generated functor type has a public operator() member function that implements the body of the lambda.

系统自带比较器:
greater, less

int main() {
    vector<int> a= {1,2,4,6,2,3,4,6};
    std::sort(a.begin(),a.end(),std::greater<int>());
    for (int& num : a) {
        cout << num << endl;
    }
}

6
6
4
4
3
2
2
1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
推荐的C++面向对象编程的书籍有以下几本: 1. 《C++ Primer》:这本书是C++编程的经典教材之一,全面介绍了C++的基本知识和面向对象编程的概念和技巧。\[1\] 2. 《面向对象程序设计》(作者:杜茂康):这本书详细介绍了C++的面向对象编程的内容,包括类、对象、继承、多态性、虚函数、重载等。同时还介绍了C++的一些高级特性和应用,如I/O流类库、模板与STL、C++ Windows程序的结构等。\[2\] 3. 《C++面向对象程序设计》(作者:刘伟):这本书系统地介绍了C++面向对象编程的基本概念和方法,包括封装、继承、多态等。同时还介绍了C++的一些高级特性和应用,如操作符重载、面向接口编程和模板等。\[3\] 这些书籍都是经典的C++面向对象编程教材,适合不同层次的读者,无论是初学者还是有一定编程经验的人都可以从中获得丰富的知识和技巧。 #### 引用[.reference_title] - *1* *3* [清华大学出版社-图书详情-《C++面向对象程序设计(第2版)》](https://blog.csdn.net/weixin_39530269/article/details/117094822)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [发现一本C++面向对象编程的好书](https://blog.csdn.net/PerfeyCui/article/details/122726546)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值