《c++入门经典》笔记14

第十四章 高级函数

14.1重载成员函数

​ 函数可以进行重载,成员函数(成员方法)实质上也是一种函数,所以成员函数也可以进行重载。

程序清单14.1 Rectangle.cpp

#include <iostream>

class Rectangle
{
private:
    int width;
    int height;

public:
    Rectangle(int width, int height);
    ~Rectangle(){};

    void drawShape() const;
    void drawShape(int width, int height) const;
};

Rectangle::Rectangle(int width, int height)
{
    this->height = height;
    this->width = width;
}

void Rectangle::drawShape() const
{
    drawShape(width, height);
}

void Rectangle::drawShape(int width, int height) const
{
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            std::cout << "*";
        }
        std::cout << std::endl;
    }
}

int main()
{
    Rectangle box(30, 5);
    std::cout << "drawShape():" << std::endl;
    box.drawShape();
    std::cout << "\ndrawShape(40,2):" << std::endl;
    box.drawShape(40, 2);
    return 0;
}

​ 编译器根据参数的类型和数值决定调用哪个版本。

14.2使用默认值

​ 常规函数可以有一个或多个默认值,类的成员函数也是如此。声明默认值的规则也相同。

程序清单14.2 Rectangle2.cpp

#include <iostream>

class Rectangle
{
private:
    int width;
    int height;

public:
    Rectangle(int weight, int height);
    ~Rectangle() {}
    void drawShape(int aWidth, int aHeight, bool useCurrentValue = false) const;
};

Rectangle::Rectangle(int width, int height)
{
    this->width = width;
    this->height = height;
}

void Rectangle::drawShape(int aWidth, int aHeight, bool useCurrentValue) const
{
    int printWidth = 0;
    int printHeight = 0;

    if (useCurrentValue == true)
    {
        printWidth = width;
        printHeight = height;
    }
    else
    {
        printWidth = aWidth;
        printHeight = aHeight;
    }

    for (int i = 0; i < printHeight; i++)
    {
        for (int j = 0; j < printWidth; j++)
        {
            std::cout << "*";
        }
        std::cout << std::endl;
    }
}

int main()
{
    Rectangle box(20, 5);
    std::cout << "drawShape(0,0,true) ..." << std::endl;
    box.drawShape(0, 0, true);
    std::cout << "drawShape(25,4) ..." << std::endl;
    box.drawShape(25, 4);
    return 0;
}

14.3初始化对象

​ 与成员函数一样,构造函数也可以重载。

​ 可以重载构造函数,但不能重载析构函数。析构函数的签名总是这样的:名称为类名前加~,且不接受任何参数。

​ 构造函数由两部分组成:初始化部分和函数体。可在初始化部分设置成员变量(即初始化列表),也可在构造函数的函数体内设置。初始化列表举例:

Tricycle::Tricycle():speed(5),wheelSize(12)
{
    //函数体
}

​ 初始化成员变量的效率比在函数体内给他们赋值高。

14.4复制构造函数

​ 又称拷贝构造函数

​ 除提供默认构造函数和析构函数之外,编译器还提供一个默认复制构造函数。每当创建对象的备份时,都将调用复制构造函数。

​ 按值将对象传入或传出函数时,都将创建对象的一个临时备份。如果对象是用户定义的,就将调用相应类的复制构造函数。

​ 所有复制构造函数都接受一个参数:一个引用,它指向所属类的对象。最好将该引用声明为常量,因为复制构造函数不用修改传入的对象,例如:Tricycle(const Tricycle &trike);

默认构造函数只作为参数传入的对象的每个成员变量复制到新对象中,这称为浅复制(浅拷贝)。虽然对大多数成员变量来说没问题,但是不适用于成员变量是指向堆中对象的指针这种情况。

浅复制将一个对象的成员变量的值复制到另一个对象中,这导致两个对象中的指针指向相同的内存地址。另一方面,深复制将堆内存中的值复制到新分配的堆内存中。

​ 浅复制使得两个或多个变量指向相同内存,如果当中一个不再在作用域内,就会导致导致调用析构函数释放分配的内存,而剩下的变量仍指向该内存,试图访问该内存将导致程序崩溃。

​ 对于这种问题,解决方案是自定义复制构造函数,并在复制时正确分配内存。

程序清单14.3 DeepCopy.cpp

#include <iostream>

class Tricycle
{
private:
    int *speed;

public:
    Tricycle();
    Tricycle(const Tricycle &);
    ~Tricycle();
    int getSpeed() const { return *speed; }
    void setSpeed(int newSpeed) { *speed = newSpeed; }
    void pedal();
    void brake();
};

Tricycle::Tricycle()
{
    speed = new int;
    *speed = 5;
}

Tricycle::Tricycle(const Tricycle &rhs)
{
    speed = new int;
    *speed = rhs.getSpeed();
}

Tricycle::~Tricycle()
{
    delete speed;
    speed = NULL;
}

void Tricycle::pedal()
{
    setSpeed(*speed + 1);
    std::cout << "\nPedaling " << getSpeed() << " mph" << std::endl;
}

void Tricycle::brake()
{
    setSpeed(*speed - 1);
    std::cout << "\nPedaling " << getSpeed() << " mph" << std::endl;
}

int main()
{
    std::cout << "Creating trike named wichita ...";
    Tricycle wichita;
    wichita.pedal();
    std::cout << "Creating trike named dallas ..." << std::endl;
    Tricycle dallas(wichita);
    std::cout << "wichita's speed: " << wichita.getSpeed() << std::endl;
    std::cout << "dallas's speed: " << dallas.getSpeed() << std::endl;
    std::cout << "setting wichita to 10 ..." << std::endl;
    wichita.setSpeed(10);
    std::cout << "wichita's speed: " << wichita.getSpeed() << std::endl;
    std::cout << "dallas's speed: " << dallas.getSpeed() << std::endl;
    return 0;
}

14.5编译阶段常量表达式

​ c++编译器竭尽所能地提高程序的运行速度——尽可能对编写的代码进行优化。一种存在效率提高空间的简单情况是将两个常量相加,如下所示:

const int decade = 10;
int year = 2016 + decade;

​ 2016与decade都是常量,所以编译器将计算这个表达式并将结果2026存储。因此,在编译器看来,就像是将2026赋给了year一样。

函数可使用const来返回常量值,如下所示:

const int getCentury()
{
    return 100;
}

你可能会认为如果调用语句int year = 2016 + getCentury();编译器会对表达式存在优化空间;虽然这个成员函数返回的是一个常量,但这个函数本身不是const的,它可能修改全局变量或调用非const成员函数。

常量表达式是c++新增的功能,用关键字constexpr表示:

constexpr int getCentury()
{
    return 100;
}

常量表达式必须返回一个表达式,而该表达式只能包含字面值、其他常量表达式或使用constexpr定义的变量。

程序清单14.4 Circle.cpp

#include <iostream>

constexpr double getPi()
{
    return (double)22 / 7; //获取Π的近似值
}

int main()
{
    float radius;
    std::cout << "Enter the radius of the circle: ";
    std::cin >> radius;
    double area = getPi() * (radius * radius);
    std::cout << "\nCircle's area: " << area << std::endl;
    return 0;
}

posted @ 2020-07-19 19:00  小立哥  阅读(233)  评论(0编辑  收藏  举报