C++ Primer Plus(第6版) 全书重点学习笔记

news2024/9/22 19:35:32

目录

第10章 对象和类

10.1 过程性编程和面向对象编程

10.2 抽象和类

10.2.1 类简介

10.2.2 实现类成员函数

 10.3 类的构造函数和析构函数

10.3.1 声明和定义构造函数

10.3.2 使用构造函数

10.3.3 默认构造函数

10.3.4 析构函数

10.4 this指针

10.5 对象数组

10.6 类作用域

10.7 抽象数据类型

第11章 对象和类

11.1 运算符重载

11.2 计算时间: 一个运算符重载示例

11.3 友元

11.4 重载运算符: 作为成员函数还是非成员函数

11.5 再谈重载: 一个矢量类

11.6 类的自动转换和强制类型转换


第10章 对象和类

10.1 过程性编程和面向对象编程

10.2 抽象和类

10.2.1 类简介

接下来定义类。 一般来说, 类规范由两个部分组成。

  • 类声明: 以数据成员的方式描述数据部分, 以成员函数(被称为方法) 的方式描述公有接口。
  • 类方法定义: 描述如何实现类成员函数。
// stock00.h -- Stock class interface
// version 00
#ifndef STOCK00_H_
#define STOCK00_H_
#include <string>  

// class声明
class Stock  
{
private: 
    std::string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() { total_val = shares * share_val; }
public:
    void acquire(const std::string & co, long n, double pr);
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show();
};    // note semicolon at the end

#endif
// 创建两个Stock对象, 它们分别名为sally和solly
Stock sally;
Stock solly;

程序解释:

(1)Stock是这个新类的类型名。 该声明让我们能够声明Stock类型的变量——称为对象或实例。接下来, 要存储的数据以类数据成员(如company和shares) 的形式出现。 例如, sally的company成员存储了公司名称, share成员存储了Sally持有的股票数量, share_val成员存储了每股的价格, total_val成员存储了股票总价格。 同样, 要执行的操作以类函数成员(方法, 如sell()和update( )) 的形式出现。 成员函数可以就地定义(如set_tot( )) , 也可以用原型表示(如其他成员函数) 。

(2)无论类成员是数据成员还是成员函数, 都可以在类的公有部分或私有部分中声明它。 但由于隐藏数据是OOP主要的目标之一, 因此数据项通常放在私有部分, 组成类接口的成员函数放在公有部分

(3)不必在类声明中使用关键字private, 因为这是类对象的默认访问控制。

10.2.2 实现类成员函数

类还有两个特殊的特征:

  • 定义成员函数时, 使用作用域解析运算符(::) 来标识函数所属的类;
  • 类方法可以访问类的private组件。
// stock00.cpp -- implementing the Stock class
// version 00
#include <iostream>
#include "stock00.h"

using namespace std;

void Stock::acquire(const std::string & co, long n, double pr)
{
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

void Stock::buy(long num, double price)
{
     if (num < 0)
    {
        std::cout << "Number of shares purchased can't be negative. "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more than you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    std::cout << "Company: " << company
              << "  Shares: " << shares << '\n'
              << "  Share Price: $" << share_val
              << "  Total Worth: $" << total_val << '\n';
}

程序解释:

(1)其定义位于类声明中的函数都将自动成为内联函数, 因此Stock::set_tot( )是一个内联函数。 类声明常将短小的成员函数作为内联函数, set_tot( )符合这样的要求。

(2)所创建的每个新对象都有自己的存储空间, 用于存储其内部变量和类成员; 但同一个类的所有对象共享同一组类方法, 即每种方法只有一个副本。 

 10.3 类的构造函数和析构函数

        C++提供了一个特殊的成员函数——类构造函数, 专门用于构造新对象、 将值赋给它们的数据成员。 更准确地说, C++为这些成员函数提供了名称和使用语法, 而程序员需要提供方法定义。 名称与类名相同。 例如, Stock类一个可能的构造函数是名为Stock( )的成员函数。 构造函数的原型和函数头有一个有趣的特征——虽然没有返回值,但没有被声明为void类型。 实际上, 构造函数没有声明类型。

10.3.1 声明和定义构造函数

        构造函数声明和定义如下:

// 构造函数声明    
Stock();        // default constructor
Stock(const std::string & co, long n = 0, double pr = 0.0);

// 构造函数定义
Stock::Stock()        // default constructor
{
    std::cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const std::string & co, long n, double pr)
{
    std::cout << "Constructor using " << co << " called\n";
    company = co;

    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                   << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

程序注释:

(1)构造函数的声明中:第一个参数是指向字符串的指针, 该字符串用于初始化成员company。 n和pr参数为shares和share_val成员提供值。 注意, 没有返回类型。 原型位于类声明的公有部分。

(2)程序声明对象时, 将自动调用构造函数

(3)如下的表示是错误的。 构造函数的参数表示的不是类成员, 而是赋给类成员的值。 因此, 参数名不能与类成员相同。为避免这种混乱, 一种常见的做法是在数据成员名中使用m_前,另一种常见的做法是, 在成员名中使用后缀_。

Stock(const string & company, long share, double share)
{
    ...
}

10.3.2 使用构造函数

        C++提供了两种使用构造函数来初始化对象的方式。 第一种方式是显式地调用构造函数,另一种方式是隐式地调用构造函数。

// 显式地调用构造函数
Stock garment = Stock("Furry Mason", 50, 2.5);
// 隐式地调用构造函数
Stock garment("Furry Mason", 50, 2.5);

下面是将构造函数与new一起使用的方法,这条语句创建一个Stock对象, 将其初始化为参数提供的值, 并将该对象的地址赋给pstock指针。 在这种情况下, 对象没有名称, 但可以使用指针来管理该对象。

Stock *pstock = new Stock("Electroshock Games", 18, 19.0);

10.3.3 默认构造函数

(1)默认构造函数是在未提供显式初始值时, 用来创建对象的构造函数。

(2)如果没有提供任何构造函数, 则C++将自动提供默认构造函数。

(3)当且仅当没有定义任何构造函数时, 编译器才会提供默认构造函数。 为类定义了构造函数后, 程序员就必须为它提供默认构造函数。 如果提供了非默认构造函数(如Stock(const char * co, int n, doublepr)) , 但没有提供默认构造函数,(Stock stock1;)声明将出错。
(4)定义默认构造函数的方式有两种。 一种是给已有构造函数的所有参数提供默认值,另一种方式是通过函数重载来定义另一个构造函数——一个没有参数的构造函数。
(5)用户定义的默认构造函数通常给所有成员提供隐式初始值。

Stock::Stock()        // default constructor
{
    std::cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

(6)在设计类时, 通常应提供对所有类成员做隐式初始化的默认构造函数

(7)不要被非默认构造函数的隐式形式所误导。如下声明,第一个声明调用非默认构造函数, 即接受参数的构造函数; 第二个声明指出, second( )是一个返回Stock对象的函数。 隐式地调用默认构造函数时, 不要使用圆括号。

Stock first("Concreate Conglomerate");
Stock second();    //错误表达,不要()
Stock third;    //调用默认构造函数

10.3.4 析构函数

(1)析构函数完成清理工作。 如果构造函数使用new来分配内存, 则析构函数将使用delete来释放这些内存。
(2) 析构函数的名称在类名前加上~。 因此, Stock类的析构函数为~Stock( )。 另外, 和构造函数一样, 析构函数也可以没有返回值和声明类型。 与构造函数不同的是, 析构函数没有参数, 因此Stock析构函数的原型必须是这样的:

// 析构函数原型
~Stock();

// 析构函数不承担任何重要的工作
Stock::~Stock()
{
}

// 析构函数执行语句
Stock::~Stock()
{
    cout << "Bye, " << company << "!\n";
}

(3)什么时候调用析构函数由编译器决定。

  • 如果创建的是静态存储类对象, 则其析构函数将在程序结束时自动被调用。
  • 如果创建的是自动存储类对象, 则其析构函数将在程序执行完代码块时自动被调用。
  • 如果对象是通过new创建的, 则它将驻留在栈内存或自由存储区中, 当使用delete来释放内存时, 其析构函数将自动被调用。
  • 最后, 程序可以创建临时对象来完成特定的操作, 在这种情况下,程序将在结束对该对象的使用时自动调用其析构函数。
  • 由于在类对象过期时析构函数将自动被调用, 因此必须有一个析构函数。 如果程序员没有提供析构函数, 编译器将隐式地声明一个默认析构函数, 并在发现导致对象被删除的代码后, 提供默认析构函数的定义。

 (4)构造函数匹配

Stock hot_tip = {"Derivatives Plus",100,45.0};
Stock jock {"sport Age Storage,Inc"};
Stock temp {};

在前两个声明中, 用大括号括起的列表与构造函数匹配,因此, 将使用该构造函数来创建这两个对象,创建对象jock时, 第二和第三个参数将为默认值0和0.0。 第三个声明与默认构造函数匹配,因此将使用该构造函数创建对象temp。

10.4 this指针

        例如:定义一个成员函数, 它查看两个Stock对象, 并返回股价较高的那个对象的引用。比较的
方法的原型如下:

const Stock & topval(const Stock & s) const;

该函数隐式地访问一个对象, 而显式地访问另一个对象, 并返回其中一个对象的引用。 括号中的const表明, 该函数不会修改被显式地访问的对象; 而括号后的const表明, 该函数不会修改被隐式地访问的对象。由于该函数返回了两个const对象之一的引用, 因此返回类型也应为const引用。
        假设要对Stock对象stock1和stock2进行比较, 并将其中股价总值较高的那一个赋给top对象, 则可以使用下面两条语句之一:

top = stock1.topval(stock2);
top = stock2.topval(stock1);

第一种格式隐式地访问stock1, 而显式地访问stock2; 第二种格式显式地访问stock1, 而隐式地访问stock2。

(1)每个成员函数(包括构造函数和析构函数) 都有一个this指针。 this指针指向调用对象。 如
果方法需要引用整个调用对象, 则可以使用表达式*this。 在函数的括号后面使用const限定符
将this限定为const, 这样将不能使用this来修改对象的值。

(2)然而, 要返回的并不是this, 因为this是对象的地址, 而是对象本身, 即*this(将解除引用
运算符*用于指针, 将得到指针指向的值) 。 现在, 可以将*this作为调用对象的别名来完成前
面的方法定义

10.5 对象数组

和Stock示例一样, 用户通常要创建同一个类的多个对象,可以创建对象数组实现。

const int STKS = 4;
int main()
{
    {
        // create an array of initialized objects
        Stock stocks[STKS] = {
            Stock("NanoSmart", 12, 20.0),
            Stock("Boffo Objects", 200, 2.0),
            Stock("Monolithic Obelisks", 130, 3.25),
            Stock("Fleep Enterprises", 60, 6.5)
        };


        std::cout << "Stock holdings:\n";
        int st;
        for (st = 0; st < STKS; st++)
            stocks[st].show();
        // set pointer to first element
        const Stock* top = &stocks[0];
        for (st = 1; st < STKS; st++)
            top = &top->topval(stocks[st]);
        // now top points to the most valuable holding
        std::cout << "\nMost valuable holding:\n";
        top->show(); }
    // std::cin.get();
    return 0; 
}

10.6 类作用域

(1)在类中定义的名称(如类数据成员名和类成员函数名) 的作用域都为整个类, 作用域为整个类的名称只在该类中是已知的, 在类外是不可知的。因此, 可以在不同类中使用相同的类成员名而不会引起冲突。

(2)在定义成员函数时, 必须使用作用域解析运算符。
(3)使用类成员名时, 必须根据上下文使用直接成员运算符(. ) 、 间接成员运算符(->) 或作用域解析运算符(::)。

(4)作用域为类的常量:因为声明类只是描述了对象的形式, 并没有创建对象,在类如下声明不可行:const int Months = 12; 然而, 在类中有两种方式可以实现这个目标, 并且效果相同。

  • 第一种:用这种方式声明枚举并不会创建类数据成员。 也就是说, 所有对象中都不包含枚举。 另外, Months只是一个符号名称, 在作用域为整个类的代码中遇到它时, 编译器将用30来替换它。
class Bakery
{
private:
    enum {Months=12};
    double consts[Months];
    ...
}
  • 第二种:使用关键字static,创建一个名为Months的常量, 该常量将与其他静态变量存储在
    一起, 而不是存储在对象中。
class Bakery
{
private:
    static const int Months =12;
    double consts[Months];
    ...
}

(5)作用域内枚举(C++11)

  • C++11提供了一种新枚举, 其枚举量的作用域为类。
enum class egg{Small, Medium, Large,Jumbo};
enum class t_shirt{Small, Medium, Large, Xlarge};

默认情况下, C++11作用域内枚举的底层类型为int。 另外, 还提供了一种语法, 可用于做出不同的选择,:short将底层类型指定为short。

enum class:short pizza{Small,Medium,Large,XLarge};

10.7 抽象数据类型

        实现栈:

// stack.h -- class definition for the stack ADT
#ifndef STACK_H_
#define STACK_H_

typedef unsigned long Item;

class Stack
{
private:
    enum {MAX = 10};    // constant specific to class
    Item items[MAX];    // holds stack items
    int top;            // index for top stack item
public:
    Stack();
    bool isempty() const;
    bool isfull() const;
    // push() returns false if stack already is full, true otherwise
    bool push(const Item & item);   // add item to stack
    // pop() returns false if stack already is empty, true otherwise
    bool pop(Item & item);          // pop top into item
};
#endif
// stack.cpp -- Stack member functions
#include "stack.h"
Stack::Stack()    // create an empty stack
{
    top = 0;
}

bool Stack::isempty() const
{
    return top == 0;
}

bool Stack::isfull() const
{
    return top == MAX;
}

bool Stack::push(const Item & item) 
{
    if (top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}

bool Stack::pop(Item & item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false; 
}
// stacker.cpp -- testing the Stack class
#include <iostream>
#include <cctype>  // or ctype.h
#include "stack.h"
int main()
{
    using namespace std;
    Stack st; // create an empty stack
    char ch;
    unsigned long po;
    cout << "Please enter A to add a purchase order,\n"
        << "P to process a PO, or Q to quit.\n";
    while (cin >> ch && toupper(ch) != 'Q')
    {
        while (cin.get() != '\n')   
            continue;
        if (!isalpha(ch))
        {
            cout << '\a';
            continue;
        }
        switch(ch)
        {
             case 'A':
             case 'a': cout << "Enter a PO number to add: ";
                       cin >> po;
                       if (st.isfull())
                           cout << "stack already full\n";
                       else
                           st.push(po);
                       break;
             case 'P':
             case 'p': if (st.isempty())
                           cout << "stack already empty\n";
                       else {
                           st.pop(po);
                           cout << "PO #" << po << " popped\n";
                       }
                       break;
        }
        cout << "Please enter A to add a purchase order,\n"
             << "P to process a PO, or Q to quit.\n";
    }
    cout << "Bye\n";
    return 0; 
}

第11章 对象和类

11.1 运算符重载

运算符重载使用户能够定义多个名称相同但特征标(参数列表) 不同的函数的,这被称为函数重载或函数多态, 旨在让您能够用同名的函数来完成相同的基本操作。

运算符函数的格式如下:

operatorop(argument-list) 

operator +( )重载+运算符, operator *( )重载*运算符。 op必须是有效的C++运算符, 不能虚构一个新的符号。 例如, 不能有operator@( )这样的函数, 因为C++中没有@运算符。

C++对用户定义的运算符重载的限制:

  1. 重载后的运算符必须至少有一个操作数是用户定义的类型, 这将防止用户为标准类型重载运算符。 因此, 不能将减法运算符(-) 重载为计算两个double值的和, 而不是它们的差。 虽然这种限制将对创造性有所影响, 但可以确保程序正常运行。
  2. 使用运算符时不能违反运算符原来的句法规则。 例如, 不能将求模运算符(%) 重载成使用一个操作数。
  3. 不能修改运算符的优先级。 因此, 如果将加号运算符重载成将两个类相加, 则新的运算符与原来的加号具有相同的优先级。
  4. 不能创建新运算符。 例如, 不能定义operator **( )函数来表示求幂
  5. 不能重载下面的运算符。

        sizeof: sizeof运算符。
        .: 成员运算符。
        . *: 成员指针运算符。
        ::: 作用域解析运算符。
        ?:: 条件运算符。
        typeid: 一个RTTI运算符。
        const_cast: 强制类型转换运算符。
        dynamic_cast: 强制类型转换运算符。
        reinterpret_cast: 强制类型转换运算符。
        static_cast: 强制类型转换运算符。

  1. 大多数运算符都可以通过成员或非成员函数进行重载, 但下面的运算符只能通过成员函数进行重载.

        =: 赋值运算符。
        ( ): 函数调用运算符。
        [ ]: 下标运算符。
        ->: 通过指针访问类成员的运算符。

11.2 计算时间: 一个运算符重载示例

// mytime0.h -- Time class before operator overloading
#ifndef MYTIME0_H_
#define MYTIME0_H_

class Time
{
private:
    int hours;
    int minutes;
public:
    Time();
    Time(int h, int m = 0);
    void AddMin(int m);
    void AddHr(int h);
    void Reset(int h = 0, int m = 0);
    const Time Sum(const Time & t) const;
    void Show() const;
};
#endif
// mytime0.cpp  -- implementing Time methods
#include <iostream>
#include "mytime0.h"

Time::Time()
{
    hours = minutes = 0;
}

Time::Time(int h, int m )
{
    hours = h;
    minutes = m;
}

void Time::AddMin(int m)
{
    minutes += m;
    hours += minutes / 60;
    minutes %= 60;
}

void Time::AddHr(int h)
{
    hours += h;
}

void Time::Reset(int h, int m)
{
    hours = h;
    minutes = m;
}

const Time Time::Sum(const Time & t) const
{
    Time sum;
    sum.minutes = minutes + t.minutes;
    sum.hours = hours + t.hours + sum.minutes / 60;
    sum.minutes %= 60;
    return sum;
}

void Time::Show() const
{
    std::cout << hours << " hours, " << minutes << " minutes";
}

程序解释:

(1)来看一下Sum( )函数的代码。 注意参数是引用, 但返回类型却不是引用。 将参数声明为引用的目的是为了提高效率。 如果按值传递Time对象, 代码的功能将相同, 但传递引用, 速度将更快, 使用的内存将更少。

(2)然而, 返回值不能是引用。 因为函数将创建一个新的Time对象(sum) , 来表示另外两个Time对象的和。 返回对象(如代码所做的那样) 将创建对象的副本, 而调用函数可以使用它。 然而, 如果返回类型为Time &, 则引用的将是sum对象。 但由于sum对象是局部变量, 在函
数结束时将被删除, 因此引用将指向一个不存在的对象。 使用返回类型Time意味着程序将在删除sum之前构造它的拷贝, 调用函数将得到该拷贝。

(3)不要返回指向局部变量或临时对象的引用。 函数执行完毕后, 局部变量和临时对象将消失,
引用将指向不存在的数据。

11.3 友元

C++控制对类对象私有部分的访问。 通常, 公有类方法提供唯一的访问途径, 但是有时候这种限制太严格, 以致于不适合特定的编程问题。 在这种情况下, C++提供了另外一种形式的访问权限: 友元。 友元有3种:

  • 友元函数;
  • 友元类;
  • 友元成员函数。

通过让函数成为类的友元, 可以赋予该函数与类的成员函数相同的访问权限。

为何需要友元:

// 表达式和成员函数调用
A = B*2.75;
A = B.operator*(2.75);

// 表达式不对应于成员函数, 因为2.75不是Time类型的对象
A = 2.75 * B;

// 有另一种解决方式——非成员函数
A = 2.75 * B;
A = operator*(2.75,B);    //非成员函数调用匹配
Time operator*(double m, const Time & t);    //函数的原型

// 非成员函数不能直接访问类的私有数据,友元函数可以访问类的私有成员
friend Time operator*(double m, const Time & t);    //原型声明前加上关键字friend:

(1)创建友元

friend Time operator*(double m, const Time & t);    //原型声明前加上关键字friend:
  • 虽然operator *( )函数是在类声明中声明的, 但它不是成员函数, 因此不能使用成员运算符来调用;
  • 虽然operator *( )函数不是成员函数, 但它与成员函数的访问权限相同。

(2)常用的友元: 重载<<运算符

cout << trip;
void operator<<(ostream & os, const Time & t)
{
    os << t.hours << "hours," << t.minutes << "minutes";
}
cout << "Trip time:" << trip << "(Tuesday)\n";
// 修改operator<<( )函数, 让它返回ostream对象的引用即可
ostream &operator<<(ostream & os, const Time & t)
{
    os << t.hours << "hours," << t.minutes << "minutes";
    return os;
}
friend Time operator*(double m, const Time & t)
{
    return t*m;
}

friend std::ostream & operator<<(std::ostram & os, const Time & t);
std::ostream & operator<<(std::ostream & os, const Time & t)

11.4 重载运算符: 作为成员函数还是非成员函数

对于很多运算符来说, 可以选择使用成员函数或非成员函数来实现运算符重载。

Time operator+(const Time & t) const;
friend Time operator+(const Time & t1, const Time & t2);

加法运算符需要两个操作数。 对于成员函数版本来说, 一个操作数通过this指针隐式地传递, 另一个操作数作为函数参数显式地传递; 对于友元版本来说, 两个操作数都作为参数来传递。

11.5 再谈重载: 一个矢量类

11.6 类的自动转换和强制类型转换

// stonewt1.h -- revised definition for the Stonewt class
#ifndef STONEWT1_H_
#define STONEWT1_H_
class Stonewt
{
private:
    enum {Lbs_per_stn = 14};      // pounds per stone
    int stone;                    // whole stones
    double pds_left;              // fractional pounds
    double pounds;                // entire weight in pounds
public:
    Stonewt(double lbs);          // construct from double pounds
    Stonewt(int stn, double lbs); // construct from stone, lbs
    Stonewt();                    // default constructor
    ~Stonewt();
    void show_lbs() const;        // show weight in pounds format
    void show_stn() const;        // show weight in stone format
// conversion functions
    operator int() const;
    operator double() const;
};
#endif
// stonewt1.cpp -- Stonewt class methods + conversion functions
#include <iostream>
using std::cout;
#include "stonewt1.h"

// construct Stonewt object from double value
Stonewt::Stonewt(double lbs)
{
    stone = int (lbs) / Lbs_per_stn;    // integer division
    pds_left = int (lbs) % Lbs_per_stn + lbs - int(lbs);
    pounds = lbs;
}

// construct Stonewt object from stone, double values
Stonewt::Stonewt(int stn, double lbs)
{
    stone = stn;
    pds_left = lbs;
    pounds =  stn * Lbs_per_stn +lbs;
}

Stonewt::Stonewt()          // default constructor, wt = 0
{
    stone = pounds = pds_left = 0;
}

Stonewt::~Stonewt()         // destructor
{
}

// show weight in stones
void Stonewt::show_stn() const
{
    cout << stone << " stone, " << pds_left << " pounds\n";
}

// show weight in pounds
void Stonewt::show_lbs() const
{
    cout << pounds << " pounds\n";
}

// conversion functions
Stonewt::operator int() const
{
    return int (pounds + 0.5);
}

Stonewt::operator double()const
{
    return pounds; 
}}

(1)只有接受一个参数的构造函数才能作为转换函数。 下面的构造函数有两个参数, 因此不能用来转换类型,然而, 如果给第二个参数提供默认值, 它便可用于转换int。

Stonewt(int stn, double lbs);
Stonewt(int stn, double lbs = 0);

(2)只接受一个参数的构造函数定义了从参数类型到类类型的转换。 如果使用关键字explicit限定
了这种构造函数, 则它只能用于显示转换, 否则也可以用于隐式转换。

// 声明构造函数
explict Stonewt(double lbs);

// 上述将关闭隐式转换, 但仍然允许显式转换, 即显式强制类型转换
Stone myCat;
myCat = 19.6;    // ×
maCat = Stonewt(19.6);    // √
myCat = (Stonewt) 19.6;    // √

(3)函数原型化提供的参数匹配过程, 允许使用Stonewt(double) 构造函数来转换其他数值类型。 也就是说, 下面两条语句都首先将int转换为double, 然后使用Stonewt(double) 构造函
数。当且仅当转换不存在二义性时, 才会进行这种二步转换。 也就是说, 如果这个类还定义了构造函数Stonewt(long) , 则编译器将拒绝这些语句, 可能指出: int可被转换为long或double

(4)转换函数:可以将Stonewt对象转换为double值。转换为typeName类型, 需要使用这种形式的转换函数:

operator typeName();

请注意以下几点:

  • 转换函数必须是类方法;
  • 转换函数不能指定返回类型;
  • 转换函数不能有参数。

总之, C++为类提供了下面的类型转换。

  • 只有一个参数的类构造函数用于将类型与该参数相同的值转换为类类型。 例如, 将int值赋给Stonewt对象时, 接受int参数的Stonewt类构造函数将自动被调用。 然而, 在构造函数声明中使用explicit可防止隐式转换, 而只允许显式转换。
  • 被称为转换函数的特殊类成员运算符函数, 用于将类对象转换为其他类型。 转换函数是类成员, 没有返回类型、 没有参数、 名为operator typeName( ), 其中, typeName是对象将被转换成的类型。将类对象赋给typeName变量或将其强制转换为typeName类型时,该转换函数将自动被调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/431540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[长安杯 2021学生组]baigei

Index 前言介绍漏洞 利用思路利用过程一.编写交互函数二.填充Tcache Bin三.释放Tcache Bin四.获取Libc地址五.Tcache Bin Attack六.完整EXP&#xff1a; 前言 最近有点迷茫&#xff0c;开始放松自己了。 心态还不是很对&#xff0c;需要继续调整。 介绍 本题是一题经典的堆题…

Java学习笔记:内部类,静态内部类,匿名内部类

​这是本人学习的总结&#xff0c;主要学习资料如下 疯狂Java讲义第三版&#xff0c;李刚编&#xff0c;电子工业出版社出版 目录 1、内部类1.1、内部类简介1.2、内部类与外部类的关系和区别&#xff1a;1.3、内部类的语法 2、 非静态内部类3、静态内部类4、匿名内部类 1、内部…

“链引擎”入驻案例 | 每天超过35万条存证上链,长安链支撑链上价值流动

引言 长安链“链引擎”计划&#xff08;Powered by Chainmaker&#xff09;(简称&#xff1a;PBC计划)是由长安链生态联盟发起的一项应用赋能计划&#xff0c;旨在以长安链技术体系为核心支撑&#xff0c;汇聚产业各方力量&#xff0c;为应用方提供技术、品牌、生态等支持&…

Keil系列教程03_主窗口和工具栏详细说明

1写在前面 本文先让大家简单认识一下Keil的主窗口界面&#xff0c;然后再进一步认识Keil的文件、编译和调试工具栏。 Toolbars工具栏就是在菜单下面的两行快捷图标按钮&#xff0c;这些快捷按钮之所以在工具栏里面&#xff0c;在于它们使用的频率较高。比如保存按钮、编译按钮…

ChatGPT智能AI对话软件

ChatGPT智能AI的市场前景非常广阔&#xff0c;因为随着人工智能技术的不断发展和应用&#xff0c;人们对于智能AI对话系统的需求也越来越大。未来&#xff0c;智能AI对话系统将在各个领域得到广泛应用&#xff0c;例如智能客服、智能家居、自动驾驶等等&#xff0c;这些都有助于…

STM32 HAL库PID控制电机 第二章 TB6612FNG芯片驱动GB37-520电机

STM32 HAL库PID控制电机 第二章 TB6612FNG芯片驱动GB37-520电机(HAL库) 1 电路图 2 TB6612简介 TB6612是双驱动&#xff0c;可同时驱动两个电机 STBY&#xff1a;接单片机的IO口清零电机全部停止&#xff0c;置1通过AIN1 AIN2&#xff0c;BIN1&#xff0c;BIN2 来控制正反转…

linux下静态库和动态库的制作

一.静态库的制作 linux下库的命名规则&#xff1a;在linux下以libXXX.a为命名&#xff0c;lib&#xff08;library&#xff09;前缀是固定的&#xff0c;代表这个是库。接下来介绍静态库的制作流程。 1.1通过gcc编译获得.o文件 一般源程序经过预处理完成头文件和宏的展开&am…

运行时内存数据区之虚拟机栈——局部变量表

这篇内容十分重要,文字也很多,仔细阅读后,你必定有所收获! 基本内容 与程序计数器一样&#xff0c;Java虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;也是线程私有的&#xff0c;它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型&#xf…

我想知道,就目前形势而言,学java好还是C++好?

前言 就现实点看看&#xff0c;可以对比现在Java和C的市场占有率&#xff0c;可以看到&#xff0c;到目前为止&#xff0c;Java在国内编程语言的市场仍然是占据着大头&#xff0c;在招聘当中Java的人数占有率仍然是遥遥领先于C&#xff0c;Java目前开阔的市场以及其巨大的岗位…

阿里,字节,拼多多,B站挨个面试一遍,你们猜哪个待遇最高?

我面试的是软件测试岗位&#xff0c;去年中旬的时候从原来的公司离职了&#xff0c;不是工作不好&#xff0c;而是公司发展速度太慢&#xff0c;自己干了几年&#xff0c;也没有太大的成长。以我目前的工作经验和实力&#xff0c;我认为准备一两个月&#xff0c;进大厂不是什么…

VS2022下载安装与基本使用(写C语言)

最近遇到一种问题&#xff0c;就是想要写一写C语言的代码&#xff0c;但是网页编辑器功能不全&#xff0c;GCC需要安装Liunx系统&#xff0c;VS又体量太大过于复杂&#xff0c;用keil又需要连接硬件&#xff0c;所以比较纠结。 工作中通常使用的是Keil&#xff0c;但是如果有时…

有记忆功能的动态通讯录

目录 前言1.进行文件操作的改造1.1contact.h的改造1.2contact.c的改造1.3test.c的改造 2.带文件操作的动态通讯录源码2.1contact.h2.2contact.c2.3test.c 总结 前言 前面我们一起学习的动态通讯录&#xff0c;一退出此程序联系人的信息就不见了&#xff1b;学习了文件操作操作后…

cocos creator 中使用web worker

1.应用场景&#xff1a;一些阻塞线程的方法可以放到worker里面去执行&#xff0c;不影响主线程&#xff0c;避免页面卡顿。 啊&#xff0c;有人就会说了&#xff0c;setTimeout不就可以了吗&#xff0c;还有什么async... JS本身就是单线程设计的&#xff0c;不管你是setTimeo…

EIGRP配置邻居关系详解

1.2 EIGRP 邻居关系 1.2.1 实验目的 通过 EIGRP 邻居建立的相关实验&#xff0c;学习到如何调整 EIGRP 的 HELLO 和 HOLD 时间&#xff0c;使用 被动接口阻止不必要的邻居关系&#xff0c;认证 EIGRP 邻居&#xff0c;静态邻居的配置以及哪些参数影响 EIGRP 邻居建立。 1.2.…

【数据库】Redis数据类型详解

目录一、5种基本数据类型1. String2. List3. Hash4. Set5. ZSet二、3种特殊类型1. Bitmap2. HyperLogLog3. Geospatial index一、5种基本数据类型 Redis 共有 5 种基本数据结构&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#…

【CVPR轻量级网络】- 追求更高的FLOPS(FasterNet)

文章目录 题目&#xff1a;摘要1 介绍CNN中FLOPs的计算 2 相关工作3 PConv和FasterNet的设计3.1 偏卷积作为基本算子&#xff08;PConv)3.2 PConv后接PWConv3.3 FasterNet作为通用骨干 4实验 题目&#xff1a; Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Netw…

Android---Jetpack之Paging

目录 Paging 组件的意思 Paging 支持的架构类型 Paging 的工作原理 PositionalDataSource PagekeyedDataSource ItemKeyedDataSource BoundaryCallback Paging 组件的意思 分页加载是在应用程序开发过程中十分常见的需求&#xff0c;Paging 就是 Google 为了方便 Andr…

JAVA局域网监听软件的设计与开发

网络监听软件是提供给网络安全管理人员进行安全管理的工具&#xff0c;可以用来监视网络的状态、数据流动情况以及网络上传输的信息&#xff0c;以获取有用信息。作为黑客来说&#xff0c;通过网络监听可以获取其所需信息&#xff08;比如密码等&#xff09;&#xff1b;对黑客…

初中级Android工程师如何快速成长寻求突破

前言 写这篇文章的初衷是看到很多同学在一家公司工作了三五年&#xff0c;因为技术没有得到提升而随着年龄的增长导致不敢提出涨薪和跳槽找工作。希望这篇文章能够给这些还是初中级Android工程师的朋友一些启发。 快速成长 我们在向领导提出加薪申请或者是准备跳槽到更大的平…

概率机器学习笔记

1.单变量高斯混合分布 原书对结果的得出没有给出解释&#xff0c;我比较困惑&#xff0c;网上找到了一篇推导的帖子&#xff0c;看完就明白了。 式2.49的解释: 红框即为关键处&#xff0c;这是显而易见的期望&#xff0c;不过是条件方差的期望&#xff1a; 该证明的作者&…