C++Primer #7 类
类的定义
简单的来说类就是数据和它的操作的一种封装,内部提供接口函数
类的成员函数的声明必须在类的内部,而它的定义则既可以放在类的内部也可以放在类的外部。(一般在类内进行声明,类外实现函数定义)
定义在类内部的函数是隐式的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 类的更多相关文章
- C++ Primer 与“类”有关的注意事项总结
C++ 与"类"有关的注意事项总结(一) 1. 除了静态 static 数据成员外,数据成员不能在类体中被显式地初始化. 例如 : class First { int memi = ...
- C++Primer学习——类
我们在创建类的对象时,类不应该仅仅被声明,还应该被定义过,否则无法知道类占用了多少的内存 但是如果一个类的名字已经出现过就被认为是已经声明过了,所以允许包含自己的指针或者引用. 默认构造函数: 当类中 ...
- C++ Primer 笔记——类成员指针
1.当我们初始化一个成员指针或为成员指针赋值时,该指针并没有指向任何数据.成员指针指定了成员而非成员所属的对象,只有当解引用成员指针时,我们才提供对象信息. 2.和普通的函数指针类似,如果成员存在重载 ...
- C++ Primer 笔记——类
1.定义在类内部的函数是隐式的inline函数. 2.因为this的目的总是指向“这个”对象,所以this是一个常量指针,我们不允许改变this中保存的地址. 3.常量成员函数:允许把const关键字 ...
- 【C++ Primer 第7章】定义抽象数据类型
参考资料 1. C++Primer #7 类 Sales_data类 Sales_data.h #include<iostream> #include<string> clas ...
- C++类的成员函数的形参列表后面的const
看到(C++ Primer)类的成员函数这里,突然对成员函数形参列表后面的const感到迷惑. 因为书中开始说是修饰隐含形参this的,然后又说是声明该函数是只读的. 大为不解! 翻资料.找人讨论.. ...
- Java类的继承与多态特性-入门笔记
相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...
- 【足迹C++primer】46、动态存储类
动态存储类 StrVec Class Design StrVec Class Definition class StrVec { public: //构造函数 StrVec():elements(nu ...
- C++primer原书中的一个错误(派生类using声明对基类权限的影响)
在C++primer 第4版的 15章 15.2.5中有以下这样一段提示: "注解:派生类能够恢复继承成员的訪问级别,但不能使訪问级别比基类中原来指定的更严格或者更宽松." 在vs ...
随机推荐
- 【Vue学习笔记1】基于Vue2.2.6版本
记录一下自己关于Vue学习的过程,便于以后归纳整理以及复习. 1.下载引用vue.js 下载: npm install vue ,然后引用. 或直接线上引用: <script src=" ...
- Tether USDT 节点钱包的安装与使用
当前,在进行数字资产交易的过程中,由于各国政府的政策因素,法币成为数字资产交易的一个重要问题.在法币接入数字资产交易的过程中,通常是用某种数字资产对法币进行锚定,例如bitshares上面的许多b ...
- NESTED内部事务异常会回滚 外部事务不会回滚 ;内部事务没有异常,外部事务有异常 则整体事务都回滚
NESTED内部事务异常会回滚 外部事务不会回滚 :内部事务没有异常,外部事务有异常 则整体事务都回滚
- MT【86】两个绝对值之和最大
分析:这里只需要注意到$(|x|+|y|)_{max}=max\{|x+y|,|x-y|\}$,所以只需求$max\{|20a|,|14b|\}$ 进而变成熟悉的反解系数问题.容易知道最大值为$a=2 ...
- 【Revit API】获取链接模型中构件
话不多说,直接代码 var doc = commandData.Application.ActiveUIDocument.Document; FilteredElementCollector link ...
- 标记,上传并下载自己创建的镜像 image
1. 首先使用 docker images 查看已有镜像: 2. 获得 docker-whale 的 IMAGE ID,然后为 docker-whale 镜像 image 打上标签 Tag.使用命令: ...
- 解题:SCOI 2007 蜥蜴
题面 拆点跑最大流 所有能跑出去的点连向汇点,容量为inf 原点连向所有初始有蜥蜴的点,容量为1 每根柱子拆成两个点“入口”和“出口”,入口向出口连容量为高度的边,出口向别的有高度的柱子的入口连容量为 ...
- Project facet jst.web.jstl has not been defined.
Project facet jst.web.jstl has not been defined. 博客分类: Eclipse Project facet 原版:http://blog.csdn.ne ...
- Tensorflow图像处理
Tensorflow图像处理主要包括:调整尺寸,图像翻转,调整色彩,处理标注框. 代码如下: #coding=utf-8 import matplotlib.pyplot as plt import ...
- LeetCode 4.反转整数
给定一个 32 位有符号整数,将整数中的数字进行反转. 示例 1: 输入: 123 输出: 321 示例 2: 输入: -123 输出: -321 示例 3: 输入: 120 输出: 21 注意: ...