【C++ Primer】类
  PjuqN0S4qpGM 2023年11月02日 45 0


数据抽象和封装,数据抽象是一种依赖于接口和实现分离的编程技术。

一、定义抽象数据类型:
   C++程序员们无须可以区分应用程序的用户以及类的用户。
1. 成员函数、非成员函数、const成员函数、this对象:
  (1)成员函数:
inline int Size() const{ return m_len; }   (2)非成员函数:
istream &Read(istream &is, Sales_data &item){ *** }【Note】:
   如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内。
  (3)const成员函数:在成员函数后加上常量符号,表示不能改变调用它的对象的内容。
【Note】:
  常量对象,以及常量对象的引用或者指针都只能调用常量成员函数。
  (4)this对象:成员函数通过一个名为this的额外的隐式参数来访问调用它的对象;引用this的目的总是指向“这个”对象,所以是一个常量指针;返回this对象的函数(更新对象的整体数据):return *this

2. 构造函数、拷贝、赋值和析构:
   (1)构造函数:构造函数的任务是初始化对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

MyString() = default;//合成默认构造函数【C++11】。
MyString(const MyString &s);//构造函数。
SmallInt(int i = 0):val(i)//构造函数初始化列表。

【Note】:
1)构造函数没有返回类型,不能被声明为const。
2)当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数(合成默认构造函数,无任何实参);这种构造函数可能会执行错误的操作,所以不是安全的;有时候编译器不能为某些类合成默认的构造函数。

(2)拷贝构造函数、赋值函数、析构函数:

MyString(const MyString &s);//拷贝。
~MyString();//析构。
MyString &operator=(const MyString &s);//赋值。

拷贝:初始化变量、按值传递、返回一个对象;赋值:适用赋值操作符;析构:当对象不再存在时执行销毁的操作。

二、访问控制与封装:
   public:成员在整个程序内可被访问。
  private:封装了类的实现细节,通过成员函数访问。
一般先写public公有成员函数再写private私有成员,便于阅读程序。
【Note】:
1)适用class(成员默认是private)和struct(成员默认是public)定义类唯一的区别就是默认的访问权限。

1. 友元:
  类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元,使用friend关键字。
【Note】:
1)友元函数声明只能出现在类定义的内部。
2)一般来说,最好在类定义开始或者结束前集中声明友元。

2. 封装的益处:
1)确保用户代码不会无意间破坏封装对象的状态。
2)被封装的类的具体实现细节可以随时改变,而无需调整用户级别的的代码。

三、类的其他特性:
1. 类成员再探:
  可变数据成员:mutable【C++11】。一个可变数据成员永远不会是const,可以改变const成员函数中成员的值。

2. 返回*this的成员函数:
  将this对象作为左值返回。一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。
  基于const的重载:通过区分成员函数是否是const的,我们可以对其进行重载。因为非常量版本的函数对于常量对象不可用,所以只能在一个常量对象上调用const成员函数。

3. 类类型:
   每个类定义了唯一的类型。
  前向声明:当一个类要使用其他类时,需在改类前定义前向声明。

4. 友元再探:

class A
{
    friend class B;//友元类
    friend void B::clear();//令成员函数作为友元。
};

【Note】:
1)如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。
2)当把一个成员函数声明为友元时,我们必须明确指出该成员函数属于哪个类。

四、类的作用域:
  在类的作用域外,普通的数据和函数成员只能由对象、引用和指针使用成员访问运算符(::);在类的外部,成员的名字被隐藏起来了。
【Note】:
1)编译器处理完类中的全部声明后才会处理成员函数的定义。
2)类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后。
3)尽管类的成员被隐藏了,但我们仍然可以通过加上类名或者使用this强制访问成员。

五、构造函数再探:
1. 构造函数初始化列表:
  建议使用构造函数初始化列表,这关系到底层效率,另外更重要的是有些成员必须初始化。
  需要注意的是:构造函数初始化列表只说明用于初始化成员的值,而不限定具体执行顺序,即成员的初始化顺序与它们在类定义中的出现顺序一致。所以最好令构造函数初始值的顺序与成员声明的顺序保持一致。
【Note】:
1)如果成员是const、引用,或者属于某种未提供默认构造函数的类类型,则必须通过构造函数初始化列表为这些成员提供初值。
2)如果构造函数为所有参数都提供了默认实参,那实际上也定义了默认构造函数。

2. 默认构造函数的作用:
  当对象被默认初始化或者值初始化时自动执行默认构造函数。类必须包含一个默认构造函数以便在这些情况下使用。实际上,即使定义了其他构造函数,最好也提供一个默认构造函数。

3. 隐式的类类型转换:
  转换构造函数:构造函数只接受一个实参,则它实际上定义了为此类类型的隐式转换机制。
  抑制构造函数定义的隐式转换:将构造函数声明为explicit。

explicit A(const string &s):a(s) {};
A a("abc");//直接初始化。
a.fun(static_cast<A>(cin));//强制执行显式转换。

【Note】:
1)关键字explicit只对一个实参的构造函数有效,只能在类内定义。
2)当我们用explicit关键字声明构造函数时,它将只能以直接初始化的形式使用,而不能使用赋值初始化。

4. 聚合类:
  聚合类使得用户可以直接访问其成员,需要满足的条件如下:

  • 所有成员都是public;
  • 没有定义任何构造函数;
  • 没有类内初始值;
  • 没有基类,也没有虚函数。
struct
{
    int val;
    string s;
}

5. 字面值常量类:
  

  • 类的成员都必须是字面值类型;
  • 类必须至少包含一个constexpr构造函数。

六、类的静态成员:
  有时候类需要它的一些成员与类本身直接相关,而不是与类的某个对象保持关联。通过在成员声明之前加上static关键字。
【Note】:
1)static成员只属于类,可以被所有对象共享。不能被声明为const,也不能在static函数体内使用this指针。
2)静态成员在类内使用static声明,在类外定义(不适用static),如果想在类内初始化,必须使用常量表达式(constexpr)。使用作用域运算符或者静态成员函数访问。
3)静态成员的类型可以是不完全类型。
4)与普通成员的一个区别是,静态成员可以作为默认实参。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
PjuqN0S4qpGM