2021-01-02

c++17入门经典下

chapter11 类

定义类

class ClassName

{

};

  1. 类的所有成员是默认私有的,public 和 private 将被应用到其后的所有成员山,除非有另一个访问修饰符

  2. 只有公共成员函数可以引用私有成员

  3. 如果没有初始化成员变量,将被默认为垃圾值

  4. 在创建对象时,要设置私有成员变量值,就必须给类添加一个特定类型的公共成员函数——构造函数

  5. 在调用默认构造函数时,可以不提供实参,编译器生成的默认构造函数没有参数,其唯一的作用是创建对象。

  6. if 没有给指针类型或基本类型的成员变量指定初始值,他们就会包含垃圾值

  7. 创建对象时,没有实体,仅仅是是对象,也需要构造器

  8. 在已经有含参数的构造器的基础上,想要定义一个函数体为空的构造函数,一般使用default关键字

    Box()=default;
    Box(){}//意思一样
    
  9. 可以在头文件中定义类,构造函数原型,成员函数原型,成员变量初始化

    在源文件中引用头文件,在源文件中实现类中的方法,但是方法必须用类名来限定

// Box.h
#ifndef BOX_H
#define BOX_H
class Box
{
private:
    double length{ 1.0 };
    double width{ 1.0 };
    double height{ 1.0 };
public:
    Box(double lengthValue, double widthValue, double heightValue);
    Box = default;
    double volume();
};
#endif // !BOX_H

// Box.cpp
#include "Box.h"
#include <iostream>
//方法必须用类名来限定
Box::Box(double lengthValue, double widthValue, double heightValue)
{
    cout << "box constructor called." << endl;
    length = lengthValue;
    width = widthValue;
    height = heightValue;
}
double Box::volume()
{
    return length * width * height;
}

note: 类的构造参数只有一个参数是有问题的,因为编译器啊,会把构造函数的参数自动转换为类类型

解决方法:explicit关键字修饰构造函数原型

//头文件
#pragma once
class Cube
{
private:
    double side;
public:
    Cube(double aSide);
    double volume();
    bool hasLargerVolumeThan(Cube aCube);
};
//源文件
#include "Cube.h"
#include<iostream>

using namespace std;


Cube::Cube(double aSide) :side{ aSide }
{
    cout << "Cube one para = "<<aSide<<" constructor called" << endl;
}
double Cube::volume()
{
    return side * side * side;
}
bool Cube::hasLargerVolumeThan(Cube aCube)
{
    return volume() > aCube.volume();//比较当前对象和参数对象的大小
}

//main函数
#include<iostream>
#include"Cube.h"
using namespace std;

int main()
{
    Cube box1{ 7.0 };
    Cube box2{ 3.0 };
    if (box1.hasLargerVolumeThan(box2))
    {
        cout << "box1 is larger than box2." << endl;
    }
    else
    {
        cout << "box1 is less or equal than box2." << endl;
    }
    cout << "volume of box1 is " << box1.volume() << endl;
    //参数应该是个cube对象,编译器会将50编译成一个cube对象
    if (box1.hasLargerVolumeThan(50.0))//这种情况就是参数类型转化为类类型的情况
    {//box1并没有与体积是50进行比较,而是与边长为50的进行比较
        cout << "volume of box1 is greater than 50 " << endl;
    }
    else
    {
        cout << "volume of box1 is less or equal to 50" << endl;
    }
    return 0;
}

解决方法:

//头文件中修改原型,
#pragma once
class Cube
{
private:
    double side;
public:
    explicit Cube(double aSide);
    double volume();
    bool hasLargerVolumeThan(Cube aCube);
};

委托构造函数

一个构造函数的代码可以在初始化列表中调用同一个类中的同一个构造函数,

编译器不能区分带单个参数的构造函数,带三个参数但是后面两个参数被省略的构造函数

class Box
{
    private :
        double length{1};
        double width{1};
        double height{1};
    public:
        Box(double lv, double wv, double hv);
        explicit Box(double side);
        Box() = default;
        double volume();
};

第一个构造函数实现:

Box::Box(double lv,double wv,double hv):length{lv},width{wv},height{hv}
{
    cout<<"box constructor 1 callled."<<endl;
}

第二个构造函数创建所有边长均相等的Box对象,他可以实现为

Box::Box(double side):Box{side ,side,side}
{
    cout<<"box constructor 2 called"<<endl;
}

第二个构造函数调用了第一个构造函数,即委托构造函数,将构造工作委托给另外一个构造函数

副本构造函数&this指针

本质:复制已有对象创建新的对象

Box::Box(const Box& box):length{box.length},width{box.width},height{box.height}{}

副本构造函数仅仅创建副本 而不修改原对象,使用const &

访问私有类成员:

只读函数getXxx();

更新函数setXxx();

this指针:表示当前对象的指针,指向当前对象的地址

返回this指针的函数:

class Box
{
    private:
        double length{1.0};
        double width{1.0};
        double height{1.0};
    public:
        Box* setLength(double lv);
        Box* setWidth(double wv);
        Box* setHeight(double hv);
};

Box* Box::setLength(double lv)
{
    if(lv>0)
        length = lv;
    return this;
}

Box* Box::setWidth(double wv)
{
    if(wv>0)
        width = wv;
    return this;
}

Box* Box::setHeight(double hv)
{
    if(hv)
        height = hv;
    return this;
}
Box myBox{3.0,4.0,5.0};
myBox.setLength(-20.0)->setWidth(40.0)->setHeight(10.0);
//返回this

同样除了返回this指针,还可以返回引用

比如:

Box& Box::setLength(double y)
{
    if(lv>0)
        length = lv;
    return *this;
}
Box myBox{3.0,4.0,5.0};
myBox.setLength(-20.0).setWidth(40.0).setHeight(10.0);
//返回*this的引用,将成员函数的调用链接在一起,方法链

const对象和const成员函数

  1. const变量是不能修改其值的变量,同样,类类型也可以定义const变量——const对象

  2. const对象的任何成员变量都不能修改(const对象的任何变量本身是一个const变量,因而不能修改)

//假设length属性是public

const Box myBox{3.0,4.0,5.0};
cout<<"the length of myBox is "<<myBox.length;
myBox.length = 2.0;//报错
//只能读不能写,常量是只读的

允许从const对象myBox读取成员变量,但是试图为这种成员变量赋值或已其他方式修改其值,会导致编译错误

  1. 这种原则也适用于指向const变量的指针和对const变量的引用
Box myBox{3.0,4.0,5.0};
const Box* boxPointer = &myBox;//落地在指针上
boxPointer->length = 2;//报错,因为是指向const变量的指针,所以不能通过指针修改常量的值
//这个别名是常量,不可以修改
void printBox(const Box& box);
//虽然作为实参传递的box对象不是const对象,但是printBox()无法修改该对象的状态
  1. const成员函数

    const Box myBox{3.0,4.0,5.0};
    cout<<"myBox dims are "<<myBox.getLength()
     <<" by "<<myBox.getLength()
     <<" by "<<myBox.getHeight()<<endl;
    
    myBox.setLength(-20.0);//无法修改,报错
    myBox.setWidth(40.0);
    myBox.setHeight(10.0);
    //无法编译
    

    需要有一种方法来告诉编译器,可以调用const对象的哪些成员函数,

    解决方法:const成员函数

  • 把所有不修改对象的函数指定为const

    //源文件
    class Box
    {
    public:
      double volume() const;
      double getLength() const{return length;}//const成员函数
      double getWidth() const {return width;}
      double getHeight() const {return height;}
      
      void setLength(double lv){ if(lv>0) length=lv;}
      void setWidth(double wv){if(wv>0) width=wv;}
      void setHeight(double hv) {if(hv) height=hv;}
    }
    
  • 函数定义进行修改:

//头文件
double Box::volume() const
{
    return length*width*height;
}

对const myBox对象进行的调用都将工作,但仍然无法对const myBox对象调用setter函数

note: 对于const函数只能调用const成员函数,因此,应该将不修改对象的所有成员函数指定为const

const 正确性——放置const对象呗修改的一系列限制

对于const对象,只能调用const成员函数。

const对象必须完全不可变,所以编译器只允许调用不会修改const对象的成员函数

试图在const成员函数内部修改对象的成员变量都会导致编译错误

将成员函数指定为const,实际上会使成员函数的this指针成为const指针

重载const

可以用const函数重载一个非const版本的成员函数

对于返回某个对象封装内部数据的一部分数据的指针或引用的函数,常常进行重载

例子

class Box 
{
    private:
        double _length{1.0};
        double _width{1.0};
        double _height{1.0};
    public:
        double& length(){return _length;}//可以加上const,就变成了专用于const版本的成员函数
        double& width(){return _width;}//添加const之后,因为返回的double是基本类型,可以将&去掉
        double& height(){return _height;}
}

使用成员函数

Box box;
box.length() = 2;//将length值改为2返回别名_length=2;
//一种用来替换getter setter函数的方法

类的对象数组

  • 主函数
#include<iostream>
#include"Box.h"
using namespace std;

int main()
{
    const Box box1{ 2,3,4 };
    Box box2{ 5 };
    cout << "box1 volume=" << box1.volume() << endl;
    cout << "box2 volume=" << box2.volume() << endl;
    Box box3{ box2 };
    cout << "box3 volume=" << box3.volume() << endl;
    cout << endl;
    Box boxes[6]{ box1,box2,box3,Box{2} };
    return 0;
}
  • 头文件
#pragma once
#ifndef BOX_H
#define BOX_H
#include<iostream>
class Box
{
private:
    double length{ 1.0 };
    double width{ 1.0 };
    double height{ 1.0 };

public:
    Box(double ly, double wv, double hv);
    Box(double side);
    Box();
    Box(const Box& box);
    double volume() const;
};
#endif // !BOX_H

源文件

#include "Box.h"
#include<iostream>
using namespace std;

Box::Box(double lv, double wv, double hv) :length{ lv }, width{ wv }, height{ hv }
{
    cout << "Box constructor 1 called." << endl;
}

Box::Box(double side) : Box{ side,side,side }
{
    cout << "Box constructor 2 called" << endl;
}

Box::Box()
{
    cout << "Default constructor called" << endl;
}

Box::Box(const Box& box) :length{ box.length }, width{ box.width }, height{ box.height }
{
    cout << "Box copy constructor called" << endl;
}

double Box::volume() const
{
    return length * height * width;
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容