C++类和对象(中)

简介: C++类和对象

C++类和对象(上):https://developer.aliyun.com/article/1459438


const 静态成员


对于一个类的成员,既要实现共享,又要实现不可变,那就用const修饰。定义静态const成员时,最好在类内部初始化。


this指针


C++封装性质:将数据和方法封装在一起

数据和方法是分开存储的。每个对象拥有独立的数据,但是对象方法是共享的。每个方法调用的时候都会隐式存在一个this指针。


C++规定:


  • this指针是隐含在成员函数内的一种指针,当一个对象被创建后,他的每一个成员函数都含有一个系统自动生成的隐含指针this,用来保存这个对象的地址。
  • 成员函数通过this指针即可知道操作的数据对象是谁
  • 隐藏在每一个非静态成员函数中,静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量


使用


  • 当形参和成员变量同名的时候可以使用this来区分。
  • 在类的普通成员函数中返回对象本身,可以使用 return *this;


const


const 修饰成员函数


使用const修饰成员函数时候,const修饰this指针指向的区域,成员函数体内不可以修改奔类中对的任何普通成员变量,放成员变量类型钱用mutable修饰时候例外。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:
    int getA() const {
        a = 50;
        return a;
    }
private:
    mutable int a{0};
};
int main(int argc, char* argv[])
{
    Person p;
    cout << p.getA() << endl;
    return 0;
}

函数如果不会修改成员函数的数据,就给函数加const。表示在此函数内不会给成员函数赋值。


const 修饰对象


const修饰对象叫常对象,编译器认为普通函数都有修改成员变量的可能。

因此不能调用非const修饰的函数。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:
    void setA(int a) const
    {
        // this->a = a;
    }
    int getA() const {
        a = 50;
        return a;
    }
private:
    mutable int a{0};
};
int main(int argc, char* argv[])
{
    const Person p;
    cout << p.getA() << endl;
    return 0;
}


友元


类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部访问。但是有时候需要在类的外部访问类的私有成员,怎么办?解决方法就是使用友元函数,友元函数是一种特权函数,C++允许这个特权函数访问私有成员。程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。


普通全局函数作为友元

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend int getA(const Person &p);
public:
private:
    int a{0};
};
int getA(const Person &p)
{
    return p.a;
}
int main(int argc, char* argv[])
{
    const Person p;
    cout << getA(p) << endl;
    return 0;
}


类的成员函数作为另一个类的友元

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person;
class Dog
{
public:
    int getA(const Person &p);
};
class Person
{
    friend int Dog::getA(const Person &p);
public:
private:
    int a{0};
};
int Dog::getA(const Person &p)
{
    return p.a;
}
int main(int argc, char* argv[])
{
    const Person p;
    Dog d;
    cout << d.getA(p) << endl;
    return 0;
}


类作为另一个类的友元

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person;
class Dog
{
public:s
    int getA(const Person &p);
};
class Person
{
    friend class Dog;
public:
private:
    int a{0};
};
int Dog::getA(const Person &p)
{
    return p.a;
}
int main(int argc, char* argv[])
{
    const Person p;
    Dog d;
    cout << d.getA(p) << endl;
    return 0;
}


运算符的重载


运算符重载就是对已有的运算符重新进行定义,赋予其另外一种功能,以使用不同的数据类型。

运算符重载只是一种语法上的方便,也就是它只是另一种函数调用。

语法:函数名是由关键字operator紧跟着运算符

比如重载+ : operator+

注意:重载运算符不要更改运算符的本质。


可重载的运算符


几乎C中的所有运算符都可以重载,但是运算符重载的使用是相当受限制的。特别是不能使用C中当前没有意义的运算符,不能改变运算符的优先级,不能改变运算符的参数个数,这样的限制有意义,否则所有的这些行为产生的运算符只会混淆,而不是澄清寓意。

+ - * / % ^ & | ~ ! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* ' -> [] () new delete new[] delete[]

不能重载的运算符有: . :: .* ?: sizeof


运算符与友元函数

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
    friend Person operator+(const Person p1, const Person &p2);
public:
private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
Person operator+(const Person p1, const Person &p2)
{
    Person p;
    p.a = p1.a + p2.a;
    return p;
}
int main(int argc, char* argv[])
{
    Person p;
    Person p2;
    cout << p << endl;
    cout << p2 + p << endl;
    return 0;
}


运算符与成员函数

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person operator+(const Person &p2)
    {
        Person p;
        p.a = this->a + p2.a;
        return p;
    }

private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    Person p;
    Person p2;
    cout << p << endl;
    cout << p2 + p << endl;
    return 0;
}


重载++/–


重载++和–运算符 需要区分是前置++/–还是后置++/–;区分方式是按照参数个数区分。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person operator+(const Person &p2)
    {
        Person p;
        p.a = this->a + p2.a;
        return p;
    }
    Person operator++(int)
    {
        Person p = *this;
        this->a++;
        return p;
    }
    Person &operator++()
    {
        ++this->a;
        return *this;
    }
    Person operator--(int)
    {
        Person p = *this;
        this->a--;
        return p;
    }
    Person &operator--()
    {
        --this->a;
        return *this;
    }
private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    Person p;
    Person p2;
    cout << p << endl;
    cout << p2 + p << endl;
    cout << p++ << endl;
    cout << p << endl;
    cout << ++p2 << endl;
    cout << p-- << endl;
    cout << p << endl;
    cout << --p2 << endl;
    return 0;
}


重载->和*

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person()
    {
        cout << "Person" << endl;
    }
    ~Person()
    {
        cout << "~Person" << endl;
    }
    void show()
    {
        cout << "aaa" << endl;
    }
private:
    int a{5};
};
class SmartPoint
{
public:
    SmartPoint(Person *p)
    {
        this->p = p;
    }
    ~SmartPoint()
    {
        delete p;
    }
    Person* operator->()
    {
        return this->p;
    }
    Person& operator*()
    {
        return *this->p;
    }

private:
    Person *p;
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    SmartPoint point(new Person);
    point->show();
    (*point).show();
    return 0;
}


注意: 类中无指针成员的时候,无需重写=运算符,斗则必须重载=运算符


重载赋值运算符


指针作为类中的成员:

  • 拷贝构造函数必须自定义(默认拷贝构造是浅拷贝)
  • 必须重载=运算符(默认=运算符是浅拷贝)


如果不重写拷贝并且也不重载=运算符,编译器会给自动创建这些函数,默认的行为类似于浅拷贝。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person()
    {
        cout << "Person" << endl;
    }
    ~Person()
    {
        cout << "~Person" << endl;
    }
private:
    int a{5};
};
class SmartPoint
{
public:
    SmartPoint()
    {

    }
    SmartPoint(Person *p)
    {
        this->p = p;
    }
    ~SmartPoint()
    {
        delete p;
    }
    explicit SmartPoint(const SmartPoint &other)
    {
        p = new Person(*other.p);
    }
    SmartPoint& operator=(const SmartPoint &other)
    {
        cout << "赋值操作" << endl;
        p = new Person(*other.p);
        return *this;
    }
    Person *p;
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    SmartPoint point(new Person);
    SmartPoint point2(point);
    SmartPoint point3;
    point3 = point;
    cout << *(point.p) << endl;
    cout << *(point2.p) << endl;
    cout << *(point3.p) << endl;
    return 0;
}


重载 != 、 == 、 () 等运算符号

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person(int a) : a(a) {}
    bool operator==(const Person &other)
    {
        return this->a == other.a;
    }
    bool operator!=(const Person &other)
    {
        return this->a != other.a;
    }
    Person & operator()(int a)
    {
        this->a += a;
        return *this;
    }
private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    Person p(5);
    Person p2(6);
    Person p3(5);
    cout << (p==p2) << endl;
    cout << (p!=p3) << endl;
    Person p6(2);
    cout << p6(5) << endl;
    cout << Person(9)(5) << endl;
    return 0;
}

注意: 不要重载 && || ,因为用户无法实现 && 和 || 具有短路特性


总结: =,[],(),->操作符号只能通过成员函数进行重载,<< 和 >> 只能通过全局函数配合友元函数进行重载,不要重载&&和||操作符号


运算符 使用建议
所有一元运算符 成员
= () [] -> ->* 必须是成员
+= -= /= *= ^= &= != %= >>= <<= 成员
其他二元运算符 非成员


继承和派生


C++最终要的特性是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类型不仅拥有旧类的成员,还拥有新定义的成员。一个B类继承自A类或者称为从A类派生出B类。这样的话A类称为基类(父类),B类称为派生类(子类),派生类中的成员包含两大部分:一类是从基类继承过来的,一类是自己增加的成员,从基类继承过来的表现其共性,而新增的成员体现了其个性。


子类继承于父类


父类派生出子类


继承的访问控制


代码示例:

class Derive : 继承方式 Base [, 继承方式 Base2 ... ]
{
// ...... 
}


继承方式分类:

访问权限:

  • public:公有继承
  • protected:保护继承
  • private:私有继承


父类个数:


单继承:指每个派生类只继承了一个基类的特征。

多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特性。

注意: 子类继承父类,子类拥有父类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在派生类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限。


代码示例


public继承:在类外可以访问父类的public变量,在类内可以访问public和protected数据。私有数据在子类内外都是不可见的。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Base
{
public:
    int a{1};
protected:
    int b{2};
private:
    int c{3};
};
class Derive : public Base
{
public:
    void show()
    {
        cout << a << b << endl;
    }
};
int main(int argc, char* argv[])
{
    Derive d;
    cout << d.a << endl;
    return 0;
}


protected继承:在类内可以访问public和protected数据。类外无法访问;private是不可见数据。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Base
{
public:
    int a{1};
protected:
    int b{2};
private:
    int c{3};
};
class Derive : protected Base
{
public:
    void show()
    {
        cout << a << b << endl;
    }
};
int main(int argc, char* argv[])
{
    Derive d;
    return 0;
}


private继承:在类内可以访问public和protected数据。类外无法访问;private是不可见数据。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Base
{
public:
    int a{1};
protected:
    int b{2};
private:
    int c{3};
};
class Derive : private Base
{
public:
    void show()
    {
        cout << a << b << endl;
    }
};
int main(int argc, char* argv[])
{
    Derive d;
    d.show();
    return 0;
}


继承中的构造和析构


子类是由父类成员叠加子类新成员而成。

构造顺序是 先父类再子类,析构顺序相反,如果有对象成员则是先父类再对象成员再子类,析构顺序相反。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
class Other
{
public:
    Other()
    {
        cout << "Other" << endl;
    }
    ~Other()
    {
        cout << "~Other" << endl;
    }
};
class Base
{
public:
    Base()
    {
        cout << "Base" << endl;
    }
    ~Base()
    {
        cout << "~Base" << endl;
    }
};
class Derive : private Base
{
public:
    Derive()
    {
        cout << "Derive" << endl;
    }
    ~Derive()
    {
        cout << "~Derive" << endl;
    }
    Other o;
};
int main(int argc, char* argv[])
{
    Derive d;
    return 0;
}


继承中的同名变量和同名函数


同名变量


当父类和子类中成员变量同名时,默认会选择子类成员,如果想访问父类同名成员则必须加上父类的作用域

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
class Base
{
public:
    int num{10};
};
class Derive : public Base
{
public:
    int num{20};
};
int main(int argc, char* argv[])
{
    Derive d;
    cout << d.num << endl;
    cout << d.Base::num << endl;
    return 0;
}


C++类和对象(下):https://developer.aliyun.com/article/1459443

目录
相关文章
|
1月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
51 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
106 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
97 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
114 4
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
32 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
32 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
29 1
|
2月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
21 0
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)