类的定义

  简单的来说类就是数据和它的操作的一种封装,内部提供接口函数

  类的成员函数的声明必须在类的内部,而它的定义则既可以放在类的内部也可以放在类的外部。(一般在类内进行声明,类外实现函数定义)

  定义在类内部的函数是隐式的inline函数(内联函数)。

构造函数

  功能:初始化类对象的数据成员。无论何时只要类的对象被创建,就会执行构造函数。

  特点:构造函数的名字和类的名字相同。类可以包含有多个构造函数(类似重载函数)。不同于其他成员函数,构造函数不能被声明为const,且没有返回类型。

  默认构造函数:无需任何实参,执行默认初始化。

  合成默认构造函数:只有当类没有声明任何构造函数时,编译器才会自动隐式地定义一个默认构造函数。

class Sales_data
{
public:
Sales_data() = default;
Sales_data(std::string s):bookNo(s), units_sold(), revenue(0.0) { }
Sales_data(std::string s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p) { }
private:
std::string bookNo ; //编号
unsigned units_sold = ; //销售数量
double revenue = ; //总销售额
}; int main()
{
Sales_data item1;
Sales_data item2("wangweihao");
Sales_data item3("wangweihao", , ); print(cout, item1) << endl;
print(cout, item2) << endl;
print(cout, item3) << endl;
return ;
}

  情况1  去掉 Sales_data( ) = default;

  报错:没有定义默认构造函数,当我们自己定义了其他任何一种构造函数时,编译器就不会帮我们合成默认构造函数。这时需要加上Sales_data( ) = default;  定义默认构造函数。

  函数成员初始化的顺序:与它们在类定义中的出现顺序一致。tips:最好令构造函数初始值顺序与成员声明的顺序保持一致,尽可能避免使用某些成员初始化其他成员,而是使用传入的变量。

举例:

 class X{
int i;
int j;
public: // 错误:未定义的,i在j之前被初始化
X(int val):j(val),i(j){}
};

类内定义构造函数:

 Sales_data(const std::string& s, unsigned n, double p)
: bookNo(s), units_sold(n), revenue(n * p){}

先执行初始值列表 bookNo(s), units_sold(n), revenue(n * p)  , 再执行{} 内函数体的内容。

委托构造函数(C++11)

  一个委托构造函数使用它所属类的其他构造函数来执行它的初始化过程。加入被委托的构造函数函数体有代码的话,先执行完代码,再执行委托者的函数体。

class Sales_data {

public:
Sales_data(const std::string& s, unsigned n, double p)
: bookNo(s), units_sold(n), revenue(n * p){}
// 其余构造函数全都委托给另一个构造函数
Sales_data() : Sales_data("", , 0.0f){}
Sales_data(const std::string& s) : Sales_data(s, , 0.0f){}
Sales_data(std::istream &is): Sales_data() {read(is,*this};}
}

构造函数在数组中的使用

                

  指针如果没有指向确定值,就没有生成对应的对象,也就没有调用构造函数。

  

  new返回的是地址,所以前两个有生成对应的对象,而pArray2这个元素生成并不会导致任何对象的生成,所以这条语句, 只是生成了2个对象.

引入this

  • 成员函数通过一个名为this的额外的隐式参数来访问调用它的那个对象。
  • 任何对类成员的访问都被看作this的隐式引用。this是一个常量指针,不允许改变this中保存的地址

  const 常量成员函数:C++允许把const关键字写在函数的参数列表后面,表示this是一个指向常量对象的指针。一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。

访问与封装

  • 定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口。
  • 定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问。(隐藏了类的实现细节)

友元

  类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数称为它的友元(friend)。如果想把一个函数作为友元,只需要添加一条以friend关键字开头的函数声明即可。

  • 重载函数作为友元,尽管名字相同,但是他们依然是不同的函数。要分别对每一个函数进行声明

  • 就算在内部定义友元函数,我们也应该在外部声明它使得它可见。

    class Sales_data {
    friend std::istream& read(std::istream& is, Sales_data& item);
    friend std::ostream& print(std::ostream& os, const Sales_data& item);
    friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs);
    }
  • 当把一个成员函数声明为友元时,我们必须明确指出该成员函数属于哪个类
    class Screen {
    friend void Window_mgr::clear(ScreenIndex);
    }

访问类的成员

  

类的作用域

名字查找

  • 首先,在名字所在的块中寻找其声明语句,只考虑在名字使用之前出现的声明

  • 如果没找到,继续查找外层作用域

  • 如果最终没有找到匹配的报错

类的声明

class Screen;     // Screen 类的声明    

  只声明类而暂时不定义它。称为前向声明,对于类而言,在它声明之后、定义之前是一个不完全类型

应用场景:

  • 定义指向这种类型的指针或引用
  • 声明(但不能定义)以不完全类型作为参数/ 返回类型的函数

Tips:因为只有当类全部完成后类才算被定义,所以一个类的成员不能是该类自己。  然而,一个类的名字出现后,它被认为是声明    过了,因此类允许包含指向自身类型的引用或指针。

ex:定义一对类X和Y,其中X包含一个指向Y的指针,而Y包含一个类型为X的对象。

class Y;
class X{
Y* y = nullptr;
}
class Y{
X x;
}

类的静态成员

  有的时候类需要它的一些成员与类本身直接相关,而不是与类的各个对象保持关联。如:一个银行账户类需要一个数据成员来表示当前的基准利率。

  • 静态成员可以是不完全类型。
  • 一个静态数据成员只能定义一次
  • sizeof 运算符不会计算静态成员变量
  • class CMyclass{
    int n;
    static int s;
    }

    则 sizeof(CMyclass) = 4;

  • 注:在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。

声明静态成员

  • 静态成员变量在类内声明,且必须带static关键字;在类外初始化,且不能带static关键字
  • 静态成员函数在类内声明,且必须带static关键字;在类外实现,且不能带static关键字
    class Account{
    public:
    void calculate(){amout += amount* interestRate;}
    static double rate(){ return interestRate}
    static void rate(double);
    private:
    std::string owner;
    double amount;
    static double interestRate;
    static double initRate();
    }

使用类的静态成员

  通过作用域运算符::直接访问静态成员

double r;
r = Account::rate();

  

定义静态成员

void Account::rate(double newRate) // 不能带static(重复)
{
interestRate = newRate;
}

  静态成员的类内初始化

  通常情况下,类的静态成员不应该在类内初始化。然而,可以为静态成员提供const整数类型的类内初始值。不过要求静态成员必须是字面值常量类型的constexpr。初始值必须是常量表达式。

ex.找出下面的静态数据成员的声明和定义错误

// exmaple.h
class Example{
public:
static double rate = 6.5; // error: rate should be a constant expression.
static const int vecSize = ;
static vector<double> vec(vecSize);//error: we may not specify an in-class initializer inside parentheses.
};
// example.c
#include "example.h"
double Example::rate;
vector<double> Example::vec;

Fixed:

// example.h
class Example {
public:
static constexpr double rate = 6.5;
static const int vecSize = ;
static vector<double> vec;
}; // example.C
#include "example.h"
constexpr double Example::rate;
vector<double> Example::vec(Example::vecSize);

静态成员实例

  考虑一个需要随时知道矩形总数和总面积的图形处理程序。可以用全局变量来记录总数和总面积,用静态成员将这两个变量封装进类中,就更容易理解和维护。

  

     

C++Primer #7 类的更多相关文章

  1. C++ Primer 与“类”有关的注意事项总结

    C++ 与"类"有关的注意事项总结(一) 1. 除了静态 static 数据成员外,数据成员不能在类体中被显式地初始化. 例如 : class First { int memi = ...

  2. C++Primer学习——类

    我们在创建类的对象时,类不应该仅仅被声明,还应该被定义过,否则无法知道类占用了多少的内存 但是如果一个类的名字已经出现过就被认为是已经声明过了,所以允许包含自己的指针或者引用. 默认构造函数: 当类中 ...

  3. C++ Primer 笔记——类成员指针

    1.当我们初始化一个成员指针或为成员指针赋值时,该指针并没有指向任何数据.成员指针指定了成员而非成员所属的对象,只有当解引用成员指针时,我们才提供对象信息. 2.和普通的函数指针类似,如果成员存在重载 ...

  4. C++ Primer 笔记——类

    1.定义在类内部的函数是隐式的inline函数. 2.因为this的目的总是指向“这个”对象,所以this是一个常量指针,我们不允许改变this中保存的地址. 3.常量成员函数:允许把const关键字 ...

  5. 【C++ Primer 第7章】定义抽象数据类型

    参考资料 1. C++Primer #7 类 Sales_data类 Sales_data.h #include<iostream> #include<string> clas ...

  6. C++类的成员函数的形参列表后面的const

    看到(C++ Primer)类的成员函数这里,突然对成员函数形参列表后面的const感到迷惑. 因为书中开始说是修饰隐含形参this的,然后又说是声明该函数是只读的. 大为不解! 翻资料.找人讨论.. ...

  7. Java类的继承与多态特性-入门笔记

    相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...

  8. 【足迹C++primer】46、动态存储类

    动态存储类 StrVec Class Design StrVec Class Definition class StrVec { public: //构造函数 StrVec():elements(nu ...

  9. C++primer原书中的一个错误(派生类using声明对基类权限的影响)

    在C++primer 第4版的 15章 15.2.5中有以下这样一段提示: "注解:派生类能够恢复继承成员的訪问级别,但不能使訪问级别比基类中原来指定的更严格或者更宽松." 在vs ...

随机推荐

  1. javascript面向对象系列第五篇——拖拽的实现

    前面的话 在之前的博客中,拖拽的实现使用了面向过程的写法.本文将以面向对象的写法来实现拖拽 写法 <style> .test{height: 50px;width: 50px;backgr ...

  2. linux课外命令

    1.要知道linux是多少位的 在命令行输入 getconf LONG_BIT 返回32就是32位,返回64就是64位的. 2.CentOS-7-x86_64-DVD-1708.iso 这种代表64位 ...

  3. Day18-前端和后端怎么区分

    前端 - 通常是针对浏览器而开发的,是在浏览器端运行的程序,而后端 - 针对的是服务器,准确的来说应该是服务器端开发.前端开发偏向于用户体验,比较直观,服务器端开发偏向于性能. 前端和后端指的是网站建 ...

  4. 【BZOJ2067】SZN(二分,动态规划,贪心)

    [BZOJ2067]SZN(二分,动态规划,贪心) 题面 权限题额 Description String-Toys joint-stock 公司需要你帮他们解决一个问题. 他们想制造一个没有环的连通图 ...

  5. linux中eth0原何变成了eth1

    Linux的网卡由eth0变成了eth1,如何修复使用wmware安装了linux,安装成功后,使用的网卡是eth0,没有eth1.但是用过一段时间后,不知道为什么eth0无法使用,系统却自动生成了e ...

  6. java基础基础总结----- Math(随机数)

    前言:math类中感觉最好玩的应该就是随机数 代码: package com.day13.math; import java.util.Random; /** * 类说明 :Math * @autho ...

  7. yolo详解

    文章<You Only Look Once: Unified, Real-Time Object Detection>提出方法下面简称YOLO. 目前,基于深度学习算法的一系列目标检测算法 ...

  8. 项目中经常用到的JavaScript方法

    1. js切割字符串 String.split() 注意:此方法与Array.join执行的方法是相反的. 2. js把数组中所有元素放入一个字符串 Array.join()

  9. hdu 5956 The Elder

    http://acm.hdu.edu.cn/showproblem.php?pid=5956 转移方程:dp[i]=(dis[i]-dis[j])*(dis[i]-dis[j])+P+dp[j] 斜率 ...

  10. JavaScript事件模拟元素拖动

    一.前言: 最近要实现一个元素拖放效果,鼠标拖动元素并且定位元素,首先想到的是HTML5中的拖放,在HTML5中,有一个draggable属性,且有dragstart, dragover, drop等 ...