SEU C++下半部分总结 第八、九、十章(一)

类和结构体

结构体与类非常相似,都是自定义的数据类型。但不同的是,结构体中所有成员的控制访问权限都是public,而类中数据成员默认为private,也就是说,类对数据成员实现了封装,而且类还能够参与继承和多态,所以说类是面向对象程序设计的基础。

类成员的访问权限

关键字权限
private只有类中的函数和友元能访问
public所有对象都可以访问
private只有派生类可以访问

类的结构

class Person{
private:
    string name;
    int age;
public:
    Person(string n,int a): name(n),age(a){};
    
    ~Person(){};

    const string &getName() const {
        return name;
    }

    void setName(const string &name) {
        Person::name = name;
    }

    int getAge() const {
        return age;
    }

    void setAge(int age) {
        Person::age = age;
    }

};

数据域

数据域中存储类中的数据,与结构体类似,可以存储各种基础类型、指针类型和自定义类型,但是,类也不能嵌套定义。
按照面向对象程序设计的要求,数据域中的成员都应该为private(不这样操作也不错)

函数域

构造函数

构造函数是每个类必须有的成员,在使用类生成对象时,系统会自动调用类的构造函数。如果没有自定义的构造函数,系统就会自动生成一个空的构造函数。
构造函数的控制访问权限必须是public,而且函数名与类名相同,不需要返回值。
当需要用构造函数为数据赋值时,可以像上面那样使用 : 运算符,也可以在函数中赋值。

Person(string n,int a)
{
    name = n;
    age = a;
}
this指针

每个对象都会有一个this指针,这个可以看成是C++系统自动完成的,我们只要使用即可。
this表示当前对象(有点难表达),以上面的例子,我们知道,在函数传参时最好给形参取上有意义的名字,那么上面的代码就要改成:

Person(string name,int age)
{
    name = name;
    age = age;
}

这段代码显然是无法执行的,因为编译器没办法分清等号前的变量和等号后的变量有什么区别。
所以,这时就要使用this指针,表示等号前的变量是这个正在操作的对象的成员

Person(string name,int age)
{
    this->name = name;
    this->age = age;
}
复制构造函数

复制构造函数会在对象复制时,比如向函数里传参,函数返回值时用到。系统不会自动生成,必须自己定义。

Person(const Person &person){
        name = person.name;
        age = person.age;
    }

析构函数

析构函数也是每个类都有的,在类的对象消亡时,系统会调用其析构函数。
析构函数不需要参数,在函数名前加~,函数名与类名相同。

友元

我们知道,根据面向对象的程序设计思想,所有数据成员都应该被封装(可以理解成设为private),当我们想修改数据成员是应该加上getter和setter方法。

const string &getName() const {
        return name;
    }

    void setName(const string &name) {
        Person::name = name;
    }

    int getAge() const {
        return age;
    }

    void setAge(int age) {
        Person::age = age;
    }

这里稍微解释一下,getter方法名后的const表示得到的数不能再做修改,这对于保持类的封装性至关重要。

但在有些特殊情况下,我们需要更高效的访问类的数据成员,这个时候就要用到友元。

友元函数

友元函数是可以直接访问类private和protected成员的非成员函数
友元函数不是类的成员函数。

可以在以上Person类中加上一行代码(放在private区和public区都不影响)

friend void print(Person person);

然后在类外定义函数

void print(Person person){
  cout<< person.name<<person.age;
}//友元函数可以直接访问Person类中的成员

一个类可以有多个友元函数,一个函数也可以是多个类的友元。

友元类

汽车需要快速地访问发动机的状态,所以把汽车类设置为发动机的友元,即汽车类的成员函数可以直接访问发动机类的private和protected数据成员。

class Engine
{
private:
    int maxSpeed;
    string name;
public:
    friend class Car;//定义友元类,即Car的所有方法都是Engine的友元方法
    
    Engine(int maxSpeed, const string &name) : maxSpeed(maxSpeed), name(name) {}
    

    int getMaxSpeed() const {
        return maxSpeed;
    }

    void setMaxSpeed(int maxSpeed) {
        Engine::maxSpeed = maxSpeed;
    }

    const string &getName() const {
        return name;
    }

    void setName(const string &name) {
        Engine::name = name;
    }
    
};

class Car{
private:
    double lenth;
    Engine engine;
public:
    Car(double lenth, const Engine &engine) : lenth(lenth), engine(engine) {}
    
    int getMaxSpeed(){
        return engine.maxSpeed;//可以直接访问Engine的private成员
    }
};

这里要特别注意,友元类是单向的,不可传递的,a是b的友元,但b不是a的友元;a是b的友元,b是c的友元,但a不是c的友元。

组合

指在一个类中包含有其他类的对象

Class B{
    int x;
    public:
    B(int a) x(a){};
    ~B(){};
}
Class A{
    private :
    B b;
    int k;
    public :
    A(int x,int y):k(x),b(x){}
    ~A(){}
}
  • 内部类的构造函数先于A的构造函数被调用
  • 内部类的析构函数后于A的析构函数被调用
  • 初始化顺序按A中声明顺序

运算符重载

我们现在有一个直线类

class Line{
private:
    double lenth;
public:
    Line(double lenth) : lenth(lenth) {}
    Line(const Line& line){
        lenth = line.lenth;
    }

    double getLenth() const {
        return lenth;
    }

    void setLenth(double lenth) {
        Line::lenth = lenth;
    }
};

我们现在想让两条直线拼接起来,即让两条直线长度相加

Line l1(5),l2(2);

如果不使用运算符重载,我们可以写一个成员函数

public void add(const Line &line)
{
   lenth = lenth + line.lenth;
}

但这似乎有点麻烦,按照我们的习惯,如果能使用 l1 +=l2这样的形式应该更好。但+=运算符是针对基本数据类型设计的,想让它适用于Line类,我们必须改变其功能(重载运算符)。

重载算数运算符
void operator+=(const Line &line){
        lenth += line.lenth;
    }

这里重载了+=运算符,那为什么不直接重载+运算符呢?

因为+=运算符已知是在第一个数的基础上进行运算,而+运算符需要两个操作数,没有办法判断是在哪个数的基础上进行运算,所以重载+运算符就必须通过友元函数重载一个全局方法(一般考试的话应该只要求会重载+=即可)

friend Line operator + (Line &l1,Line &l2){
        Line line(0);
        line.lenth = l1.lenth+l2.lenth;
        return line;
    }
重载逻辑运算符
bool operator==(const Line &rhs) const {
        return lenth == rhs.lenth;
    }

    bool operator!=(const Line &rhs) const {
        return !(rhs == *this);
    }

    bool operator<(const Line &rhs) const {
        return lenth < rhs.lenth;
    }

    bool operator>(const Line &rhs) const {
        return rhs < *this;
    }

    bool operator<=(const Line &rhs) const {
        return !(rhs < *this);
    }

    bool operator>=(const Line &rhs) const {
        return !(*this < rhs);
    }
重载流运算符

重载流运算符是要通过友元重载一个全局函数(这个格式很固定,记住即可)

friend ostream &operator<<(ostream &os, const Line &line) {
        os << "lenth: " << line.lenth;
        return os;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值