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 ...
随机推荐
- 安装selenium和chromedriver
网上找的算法,在运行爬虫代码时,需要Selenium+Phantomjs实现,我改成了用Selenium+Chrome:针对指定网址,自动打开浏览器,输入关键词搜索,并保存搜索的内容. 1. 安装se ...
- Fantastic Graph 2018 沈阳赛区网络预赛 F题
题意: 二分图 有k条边,我们去选择其中的几条 每选中一条那么此条边的u 和 v的度数就+1,最后使得所有点的度数都在[l, r]这个区间内 , 这就相当于 边流入1,流出1,最后使流量平衡 解析: ...
- 学习4__STM32--中断
Cortex-M处理器的NVIC接收中断请求各种源 > 从图中可看出,NVIC是一个外设中断的管理器,简化core的工作,控制着整个芯片的中断功能 > NVIC负责给外设中断分配优先级,使 ...
- 进程和线程(3)-ThreadLocal
ThreadLocal 在多线程环境下,每个线程都有自己的数据.一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁. 但是局部变量也 ...
- MySql数据库类型bit等与JAVA中的对应类型【布尔类型怎么存】
用char(1):可以表示字符或者数字,但是不能直接计算同列的值.存储消耗1个字节 用tinyint:只能表示数字,可以直接计算,存储消耗2个字节 用bit: 只能表示0或1,不能计算,存储消耗小于等 ...
- Redis的持久化数据
Redis支持两种持久化:RDB和AOF模式 一.名词解释: RDB:持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot).AOF:持久化记录服务器执行的 ...
- python爬虫requests过程中添加headers
浏览器中打开页面,以edge为例,点击“查看源”或F12 第一步:点击上图中“网络”标签,然后刷新或载入页面 第二步:在右侧“标头”下方的“请求标头”中的所有信息都是headers内容,添加到requ ...
- dedecms在linux上安装提示没权限解决办法
web服务器运行的用户与目录所有者用户必须不一样,比如apache运行的用户为root,那么网站目录设置的所有者就应该不能设置为root,而是设置不同于root的用户,如apache. 我们这里假设w ...
- JS面向对象编程之对象(简化版)
上次网上看了一篇这个文章,然后乱七八糟晕头转向把我晕的够呛.看了半天没找到错的地方但是浏览器Hello world就是没有定义...我也是醉了,最后发现我认为是废话的话一句话竟然有用!!!所以我还是简 ...
- shell脚本常用参数
shell 脚本 常用参数 #!/bin/sh # 在脚本第一行脚本头 # sh为当前系统默认shell,可指定为bash等shell sh -x # 执行过程 sh -n # 检查语法 (a=bbk ...