C++---CHAPTER 7: CLASS

  • 引入this
    调用一个成员函数,编译器用请求该函数的对象地址初始化this,所以this的目的总是指向这个对象,因此this是一个常量指针。

  • 引入const成员函数
    以下是Sales_data类的一个成员函数的定义,参数列表之后的const作用是修改隐式this指针的类型,因为默认情况下,this的类型是指向类类型非常量版本的常量指针。所以默认情况不能把this绑定到一个常量对象上。这样使用const的成员函数被称为常量成员函数。

std::string isbn() const {return bookNo;}
  • 类作用域
    编译器首先编译成员的声明,然后是成员函数体,所以成员函数体可以随意使用类中的其他成员无须在意成员的顺序。

  • 定义一个返回this对象的函数
    函数类似于某个内置运算符时,应该令该函数的行为尽量模仿这个运算符。内置赋值运算符把它的左侧运算对当左值返回,意味着这些函数返回的是对象本身而非对象的副本,因此combine必须返回引用类型。

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
  units_sold += rhs.units_sold;
  revenue += rhs.revenue;
  return *this;  //解引用指针获取执行该函数的对象。
}
  • 类相关的非成员函数
    如果函数在概念上属于类但是不定义在类中,则它一般应与类声明在同一个头文件。
    默认情况下拷贝类的对象,拷贝的是对象的数据成员。
  • 构造函数
    构造函数不能声明为const,直到构造函数完成初始化过程,对象才能取得"常量"属性。因此,构造函数在const对象的构造过程中可以向其写值。
    • 合成的默认构造函数:类没有显示的定义构造函数,那么编译器会为我们隐式地定义一个合成的默认构造函数,安照类内初始值初始化成员,没有的话则默认初始化成员。只有当类没有声明任何构造函数的时候,才会默认构造函数。使用= default要求编译器生成构造函数。

    • 构造函数初始值列表:

Sales_data = (const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p*n) {}
  • 一个const成员函数如果以引用的形式返回*this指针,那么它的返回类型将是常量引用。

基于const的重载
如下,当一个成员调用另一个成员的时候,this指针在其中隐式地传递,当display的非常量版本调用do_display的时候,它的this指针隐式地从指向非常量的指针转换成指向常量的指针。

#include <iostream>
#include <string>
using namespace std;

class Screen{
public:
  typedef std::string::size_type pos;
  
  Screen() = default; // 因为要写另一个构造函数
  Screen(pos ht, pos wd, char c): height(ht), width(wd), contents(ht* wd, c) {}
  
  char get() const
  {return contents[cursor];} // 类内部声明定义的隐式内联函数
  
  inline char get(pos ht, pos wd) const; // 显示内联

  Screen &set(char);
  SCreen &set(pos, pos, char);
  
  Screen &move(pos r, pos c); //能在之后被设为内联函数

  Screen &display(std::ostream &os)
  {do_display(os); return *this;}

  const Screen &display(std::ostream &os) const
  {do_display(os); return *this;}


private:
  pos cursor = 0;  //光标的意思
  pos height = 0, width = 0;
  std::string contents;

  void do_display(std::ostream &os) const{os << contents;}

};

inline  // 在函数的定义处指定为内联函数
Screen &Screen::move(pos r, pos c)
{
  pos row = r * width;
  cursor = row + c; 
  return *this;  //左值
}

char Screen::get(pos r, pos c) const
{
  pos row = r * width;
  return contents[row + c];
}

inline
Screen &Screen::set(char c)
{
  contents[cursor] = c;
  return *this;
}

inline 
Screen &Screen::set(pos r, pos col, char ch)
{
  contents[r*width +col] =ch;
  return *this;
}

int main() {
  Screen myscreen(5, 5, 'F');
  char ch =myscreen.get();
  ch = myscreen.get(0, 0);
  cout << ch << endl;
}

注:转换成常量:指向T类型的指针或引用分别转换成指向const T的指针或引用:

int i ;
const int & j = i; //非常量转换成const int 的引用
const int *p = &i; //非常量的地址转换成const的地址
  • 类的声明
class Screen; // Screen类的声明

前项声明(是一种不完全类型)的使用:可以定义指向这种类型的指针或者引用,声明(但不能定义)以不完全类型作为参数或者返回类型的函数。
必须完成类的定义,编译器才能知道存储数据成员需要多少空间。因为只有当类全部完成后才算被定义,因此不能有一个类的成员类型是该类自己。
但是类的名字出现后,声明了该类,因此类允许包含自身类型的引用或者指针:

class Link_Screen{
  Screen window;
  Link_Screen *next;
  Link_Screen *prev;
}
  • 友元函数
    一个类指定了友元类,则友元类的成员函数可以访问此类的所有成员。

  • 类的作用域

  • 构造函数再谈

构造函数初始值列表的必要性:如果成员是const、引用,或者属于某种未提供默认构造函数的类类型,必须通过构造函数初始值列表为这些成员提供初值。

class ConstRef{
public:
  ConstRef(int ii);
private:
  int i;
  const int c;
  int &ri;
}

ConstRef::ConstRef(int ii)
{
  i = ii; //正确
  c = ii; //错误 :不能给const复制
  ri = i; //错误 :ri没被初始化
}

那么成员初始化的顺序是:与他们在类中的定义一致。

默认实参与构造函数:可以重写一个使用默认实参的构造函数

class Sales_data{
public:
  //定义默认构造函数,接受一个字符串初始值
  Sales_data(std::string s = " ") bookNo(s) {}
private:
  ....
}

  • 委托构造函数:delegating constructor,委托构造函数就是把自己的初始化全部交给其他构造函数,受委托的构造函数初始值列表和函数体执行后才轮到委托构造函数。
  • 类的静态成员:与类直接相关的成员,不包含this指针,静态成员函数不能声明为const的,不能再static函数体内使用this指针。
class Account{
  public:
    void calculate() {amount += amount * interestRate;}
    static double rate() {return interestRate;}
    static void rate(double);
  private:
    std::string owner;
    double amount;
    static constexpr int period = 30;  //period是常量表达式
    static double interestRate;
    static double initRate();
}

// static关键字出现在类内部
void Account::rate(double newRate)  
{
  interestRate = newRate;
}

由上看出,必须在内外初始化每个静态成员。
类内初始化必须要求静态成员是字面值常量类型的constexpr

  • 静态成员与普通成员的不同:
  1. 静态数据成员的类型可以就是她所属的类类型;
    2.可使用静态成员做默认实参;
class Account{
  public:
    void calculate() {amount += amount * interestRate;}
    static double rate() {return interestRate;}
    static void rate(double);
  private:
    std::string owner;
    double amount;
    static double interestRate;
    static double initRate();
}

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

相关阅读更多精彩内容

友情链接更多精彩内容