C++学习笔记 三
以下内容为C++Primer第七章的阅读笔记,包括 类
- C++学习笔记 三
- 类
- 定义抽象数据类型
- 构造函数
- 合成的默认构造函数
- 自定义构造函数
- struct与class的区别
- 构造函数
- 定义抽象数据类型
- 一些类的其他特性
- 可变数据成员
- 返回*this 的成员函数
- 友元friend
- 初始化和赋值的区别
- 委托构造函数
- 隐式的类类型转化
- 聚合类
- 字面值常量类
- 类的静态成员
- 静态对象的使用
- 静态对象的定义
- 类
类
定义抽象数据类型
以下是一段示例代码
struct Sales_data
{
std::string isbn() const
{
return bookNo;
}
Sales_data& combine(const Sales_data&);
double avg_price() const;
std::string bookNo;
unsigned units_sold = .0;
double revenue = 0.0;
}
-
成员函数的执行过程
在成员函数中利用隐式形参
this
来访问成员变量当我们调用一个类对象的成员函数的时候,编译器会隐式地将该对象的地址传递给这个成员函数地隐式形参(常量指针)
this
中,以便于成员函数调用成员变量。
比如当我们调用函数isbn()
的时候:obj.isbn()
其实际的执行过程类似于这样obj.isbn(&obj)
,因此函数isbn()
的定义过程我们还可以这样写std::string isbn() { return this->bookNo; }
-
成员函数后面
const
的作用我们首先需要明确的是,隐式形参
this
的默认类型是 指向该类类型的非常量版本的常量指针 即classType *const this
注意区别于指向常量对象的指针
const classType * cp
再回顾之前的内容,若我们申明了一个非常量引用,那我们就无法将一个常量绑定到这个引用上(反之就可以)
放在指针身上也是成立的,这样默认申明的this
形参将无法指向一个常量类对象(const Sales_type mysales
),这将导致我们无法使用调用常量类对象的成员函数,这显然会大大降低成员函数的灵活性。为了解决这个问题,我们便将
this
定义为 指向该类类型的 常量对象的 常量指针 即 再函数申明后面加上const
限定符这样定义的成员函数我们便称之为常量成员函数
-
::
作用域(命名空间)运算符,每个类就是一个作用域,因此如果在类的外部定义成员函数,则应该带上类的命名空间,如Sales_data::avg_price() const
。 -
this
也可以作为函数的返回值,如函数Sales_data& combine(const Sales_data&);
Sales_data& combine(const Sales_data& ths) { //do something //do something return *this;//返回自身对象 }
构造函数
- 直到构造函数完成初始化过程,对象才能真正取得到其“常量”的属性
合成的默认构造函数
即默认构造函数
-
该构造函数会将类里的初始值用来初始化,如没有则执行默认初始化。
-
显式的默认构造函数
Sales_data()=default;
自定义构造函数
Sales_data(const std::string &s, unsigned n): bookNo(s),units_sold(n) {}
(const std::string &s, unsigned n): bookNo(s),units_sold(n)
前面指定参数,后面是初始化列表
也可以在类的外部定义构造函数
Sales_data :: Sales_data(params)
{
//do something;
}
- 合成的默认拷贝操作:对类内每个成员变量都应用拷贝操作
struct
与class
的区别
两者都可以用来定义一个类,当用struct
定义的类所有的成员默认都是public
的访问限制,而使用class
则可以指定不同的访问限制,其余便无任何差别
在class
定义的类中,如果我们想让非成员函数访问类的private
成员变量,则使用friend
关键字在类中声明该函数。
一些类的其他特性
-
定义在类内部的成员函数默认为内联函数(
inline
),当然,如果需要的话我们可以在类内部用inline
先声明这个函数,再在类的外部实现它,可以达到同样的效果。 -
可变数据成员
关键字
mutable
。一般情况下,对于声明为const
的类对象我们是无法修改他的成员变量的值得,若我们想让某个成员变量即使在const
对象中(或类对象的const
函数中)也能被修改,则添加关节字mutable
来修饰该成员变量的声明。
返回*this
的成员函数
令一个成员函数返回其本身对象的引用可以实现让用户只使用一条语句边可以调用多个函数。例如
Sales_data &get()
{
//do something
return *this;
}
- 如果一个常量函数以应用的形式返回
*this
则其得到的将是一个常量引用,可是使用重载让用户选择是否返回常量引用
友元friend
- 将另一个类指定成自身的友元,这样允许另一个类的成员访问自己的私有成员
- 注意,友元没有传递性,友元的友元就不是自己的友元了(如果没有指定的话)
- 单独将另一个类的成员函数指定为自己的友元
- 对于一组重载函数如果都要获得私有访问权限,必须将每个函数都声明为友元
初始化和赋值的区别
初始化直接初始化数据成员,赋值则是先初始化在赋值
如果我们在构造函数中不使用初始化列表而是在它的函数体中执行赋值操作,若成员是const
引用或者是属于某种未提供默认构造函数的类类型则编译无法通过,因为没有初始化。
建议使用构造函数初始值来初始化。
委托构造函数
一个构造函数可以使用本类的某个构造函数来为他服务
隐式的类类型转化
如同内置的几种类型之间存在隐式转化的规则,当一个类的构造函数只接受一个实参的时候,我们就默认定义了一种从该实参类型转化到类类型的隐式转化规则。
比如,若Person
类存在如下构造函数:
Person() = default;
Person(const std::string & name, const std::string & addrss) : name(name), address(addrss),callCount(0) {};
Person(istream& is);
Person(const std::string& addr) :Person("", addr) {};//只接受一个实参
那我们就定义了一种从 const string&
类型隐式转化为Person
类类型的规则,即其name
成员为空字符串
-
如果我们想抑制这种由于构造函数而造成的隐式转化规则,需要添加关键字
explicit
,如explicit Person(const std::string& addr) :Person("", addr) {};//只接受一个实参,但它不会提供隐式转化规则
如果我们使用了
explicit
则其定义的构造函数只能用于直接初始化而不能执行拷贝形式的初始化(=
) -
注意,即使采用了
explict
来定义构造函数,我们还是可以通过显示类型转化(强制类型转化)来利用这个只有一个实参的构造函数,即相当于构造了一个临时的类对象。
聚合类
聚合类使得用户可以直接访问其成员
- 所有的成员都是
public
- 没有定义任何构造函数
- 没有类内初始值
- 没有基类也没有
virtual
函数
例如通过struct
定义的类
字面值常量类
略
类的静态成员
类的静态成员与类直接关联在一起,而不会与类的每个对象相关联,一旦这个静态成员发生改变,所有的类对象都能得到改变,所有的类对象共享这个静态成员。
类似的类的静态函数则不能使用this
const
因为它不和任何对象相绑定
静态对象的使用
- 通过域运算符直接访问
::
- 其余正常方法
静态对象的定义
若在类的外部定义静态成员函数,则只需要声明的时候加上static
一般情况下我们需要在类的外部定义并且初始化静态成员
- 只要在静态成员是
constexpr
类型我们可以给他指定const整数类型的
类内初始值,如果指定了类内初始值,则成员的定义则无需再指定初始值了
注意类内初始值与定义初始化是不同的
- 静态类类型可以是其所属的类型(不完全类型),然而非静态数据成员则申明成它所属类型的指针或引用
- 静态类类型可以作为默认实参