文章标题

C++基础2:类与对象

1. 认识类与对象
  • 基本概念
概念比喻
对象/实例楼房
实例化建造
建筑图纸

* 面向对象四大特征

特征说明类比
抽象抽出具体事物的普遍性的本质分门别类:鸟类、哺乳类、鱼类
封装把数据与处理(函数)包在一起通信录(增加、删除)
继承数据与处理(函数)的传承财富与绝技、混血儿(肤色/头发、 两种语言
多态同一个事物(函数)的多种形态手机键盘数字与字母、 电脑键盘功能键
2. 类的定义与对象创建
2.1 类的定义:与struct相似(C++)
class 类名{
    成员变量成员函数声明
};

class定义最后的;一定不要忘记。

  • 构成
构成作用
数据成员(data member)/成员变量/属性对象内部数据和状态,只能在类定义中声明,可以在成员函数中直接调用。
成员函数/方法对象相关的操作,可以在类内实现或类外实现。

* 实例:复数Complex

  • 作用域运算符:: – 函数归属
  • 访问限定符
限定符作用
private[默认]私有
public公开
protected保护

实践中,成员变量多数情况使用private或者protected,成员函数多数情况使用public。通常,通过成员函数改变对象的成员变量。

  • 声明与实现分离

    • 头文件 – 声明
      方式:#pragma once或者#ifnde...#endif
      作用:防止头文件二次编译
    • 源文件 – 实现
      引用头文件:#include <>(标准库函数)/#include ""(自定义/第三方函数)
  • classstruct区别
    struct_class.cpp

#include <iostream>

using std::cout;
using std::endl;

struct SPos {
    int x,y,z;
};
class CPos {
    int x,y,z;
};
int main(){
#ifdef STRUCT
    SPos spos = {1,1,1};
    cout << "(" << spos.x << "," << spos.y << "," << spos.z << ")" << endl;
#else
    CPos cpos = {1,1,1};
    cout << "(" << cpos.x << "," << cpos.y << "," << cpos.z << ")" << endl;
#endif
}```
`class`与`struct`区别是:
1. 默认的访问控制不同
 * `struct`是`public`
 * `class`是`private`
2. `struct`可以使用花括号内的初始值列表`{...}`初始化,`class`不可以(C++98不可以,C++11可以)。

> 注意:
* C++的`struct`可以有成员函数,而C不可以。
* C++的`struct`可以使用访问控制关键字(`public` `private` `protected`),而C不可以。

*成员变量默认初始化为随机值(主要影响指针)。*

##### 2.2 对象创建

* 直接定义 -- 类作为类型定义变量 -- 栈上创建

类名 对象1;// 调用默认构造函数
类名 对象2 = 对象1;// 调用复制构造函数
类名 对象3(对象1);// 调用复制构造函数

* 动态创建 -- 堆上创建

类名* 对象指针 = new 类名;// 调用默认构造函数
delete 对象指针;

int* p = new int[10];
delete p;// 只释放p[0]
delete [] p;// 释放全部数组


实例:账单

> 基本类型的初始化新增语法:

int a(0);// 等价 int a = 0;
const float b(1.0);// 等价 const float b = 1.0;


> 空结构体与空类的大小(`sizeof`)`1`,主要在于初始化/实例化时,编译器给变量/对象分配内存(地址),内存最小单位为1个字节。
通常,`sizeof(类型) == sizeof(变量)`。

> 注意:C++除了特殊情况,很少直接使用`malloc()/free()`申请释放内存,取而代之的是`new/delete`。

#### 3. `this`指针

* 作用域 -- 类内部
* 特点
 * 类的一个自动生成、自动隐藏的私有成员
 * 每个对象仅有一个`this`指针
 * 当一个对象被创建时,`this`指针就存放指向对象数据的首地址
 * 不是对象本身的一部分,不会影响sizeof(对象)的结果

> 如果成员函数形参与成员变量同名,使用`this->`做为前缀区分。

#### 4. 方法
##### 4.1 构造函数
* 语法

类名(参数){
函数体
}

 * 在对象被创建时自动执行
 * 构造函数的函数名与类名相同
 * 没有返回值类型、也没有返回值
 * 可以有多个构造函数
* 调用时机
 * 对象直接定义创建--构造函数不能被显式调用
 * `new`动态创建
* 默认构造函数
类中没有显式的定义任何构造函数,编译器就会自动为该类型生成默认构造函数
* 构造函数的三个作用
 * 给创建的对象建立一个标识符
 * 为对象数据成员开辟内存空间
 * 完成对象数据成员的初始化
* 初始化列表
 * 语法

类名(参数):成员变量(参数){
函数体
}

 * 作用
初始化非静态成员变量
 * 说明
从概念上来讲,构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
 * 必须使用初始化列表的情况
1.常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面。
2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面。
3.没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

* 初始化列表与构造函数内部成员变量赋值的区别
成员变量初始化与成员变量赋值

> 能使用初始化列表的时候尽量使用初始化列表

* 成员变量的初始化顺序

include

using std::cout;
using std::endl;
class Member1{
public:
Member1(){
cout << “Member1 Init” <

成员变量在使用初始化列表初始化时,与构造函数中初始化成员列表的顺序无关,只与定义成员变量的顺序有关。

> C++的函数可以增加默认参数。
写法:
1. 默认参数必须写在函数声明中,不能写在函数实现的参数列表中中。
2. 默认参数必须写在所有非默认参数的后面。
3. 默认参数可以写任意多个。
使用:
默认参数可以不用传递值,此时,使用默认值。
默认参数可以传值,此时,使用实参值。

##### 4.2 析构函数
* 语法:

~类名(){
函数体
}

 * 析构函数的函数名与类名相同
 * 函数名前必须有一个`~`
 * 没有参数
 * 没有返回值类型、也没有返回值
 * 只能有一个析构函数
* 调用时机
 * 对象离开作用域
 * `delete`
* 默认析构函数
类中没有显式的定义析构函数,编译器就会自动为该类型生成默认析构函数
* 作用
释放对象所申请占有的资源

##### 4.3 引用(别名)

* 语法
声明:`const 类型名& 对象名`/`类型名& 对象名`
使用:与对象变量、基本类型变量一样
* 何处使用引用
 * 函数参数列表
 * 成员变量 -- 对象初始化时,必须显示初始化的变量
* 为何使用引用
 * 避免对象复制
 * 避免传递空指针
 * 使用方便

> 类型:
* 基本类型(`bool``char``int``short``float`)
* 复合类型(指针、数组、引用)
* 自定义类型(`struct``union``class`)

引用作用:取代指针

> 引用与指针的区别
* 指针申请一块内存,它的内容是所指内存的地址;引用是某块内存的别名,不申请内存。
* 引用只能在定义时被初始化一次,之后不可变;指针可变;
* 引用不能为空,指针可以为空;
* 引用使用时无需解引用`*`,指针需要解引用;
* `sizeof 引用`得到的是所指向的变量/对象的大小,而`sizeof 指针`得到的是指针本身的大小;

引用通常用于两种情形:成员变量和函数的参数列表。

##### 4.4 拷贝/复制构造函数

* 语法: 

类名(类名& 形参){
函数体
}

或者

类名(const 类名& 形参){
函数体
}

或者

类名(const 类名 形参){
函数体std:swap();
}

* 调用时机
 * 一个对象作为函数参数,以值传递的方式传入函数体
 * 一个对象作为函数返回值,以值从函数返回
 * 一个对象用于给另外一个对象进行初始化(赋值初始化)

> 实践说明:
* gcc自动NRV(named return value)优化,不执行拷贝构造函数,需要在编译命令添加选项禁止`-fno-elide-constructors`;
* vs执行拷贝构造函数
总之,尽量不要返回局部对象。

##### 4.5 默认拷贝构造函数

* 本质:内存拷贝
* 作用:复制一个已经存在的对象

> 如何禁用默认拷贝构造函数?在类定义,添加私有的复制构造函数的声明,但不实现。

#### 5. 赋值运算符重载函数
* 语法

类名& operater=(const 类名& 形参){
// 赋值操作
return *this;
}


* 调用时机:赋值
* 默认赋值运算符重载函数:内存拷贝
* 作用:赋值

> 如何禁用默认赋值运算符重载函数?在类定义,添加私有的赋值运算符重载函数的声明,但不实现。

> 拷贝构造函数与赋值操作符的区别
* 拷贝构造函数:当一个已经存在的对象来初始化一个未曾存在的对象
* 赋值操作符:当两个对象都已经存在

#### 6. 深拷贝与浅拷贝
* 浅拷贝:只拷贝指针地址
* 深拷贝:重现分配堆内存,拷贝指针指向内容

#### 7. 友元细说
* 作用:非成员函数访问类中的私有成员
* 分类
 * 全局友元函数:将全局函数声明成友元函数
 * 友元成员函数:类的提前引用声明,将一个函数声明为多个类的友元函数
 * 友元类:将整个类声明为友元
* 特点
 * 友元关系单向性
 * 友元关系不可传递

#### 8. `const`限定符
* 本质:不可修改
* `const`与变量/对象

const 类型 变量 = 初始值;
const 类型 对象;

例如:

const int size = 4;

现在比较前卫写法

类型 const 变量 = 初始值;
类型 const 对象;

例如:

int const size = 4;


 * 定义时必须初始化
 * 全局作用域声明的`const`变量默认作用域是定义所在文件
 * `const`对象只能调用`const`成员函数

* `const`与宏定义`#define`的区别

||`const`|宏定义`#define`|
|:-|
|编译器处理方式|编译运行阶段使用|预处理阶段展开/替换|
|类型|有具体的类型|没有类型|
|安全检查|编译阶段会执行类型检查|不做任何类型检查|
|存储方式|分配内存|不分配内存|

* `const`与指针

|No.|类型|语法|作用|
|:-:|
|1|`const`指针|`类型* const 变量 = 初始值;`|指针指向地址不能改变|
|2|指向`const`对象的指针|`const 类型* 变量 = 初始值;` `类型 const* 变量 = 初始值;`|指针指向对象不能改变|
|3|指向`const`对象的`const`指针|`const 类型* const 变量 = 初始值;`|指针指向地址和对象不能改变|

> No.2 是使用最频繁的方式。

* `const`与引用

`类型 const &变量 = 初始值;`与`const 类型& 变量 = 初始值;`都是引用对象不能改变。

* `const`与函数的参数和返回值

|类型|语法|作用|说明|
|:-|
|`const`参数|`返回值类型 函数(const 类型 形参)`|函数内部不能改变参数的值|这样的参数的输入值|
|`const`返回值|`const 返回值类型 函数(形参列表)`|函数的返回值不能改变|常用于字符串/指针|

* `const`成员变量
 * 不能在类声明中初始化`const`数据成员
 * `const`成员变量只能在类构造函数的初始化列表中初始化

class 类名{
public:
类名(类型 形参):成员变量(形参){}
private:
const 类型 成员变量;
}

> 如果使用`const`成员变量不能使用赋值运算符重载函数

* `const`成员函数
成员函数不能修改类中任何成员变量。一般写在成员函数的最后来修饰。
 * 声明

class 类名{
public:
返回值类型 函数名(形参列表)const;
}

 * 定义

返回值类型 函数名(形参列表)const;


> 必须在成员函数的声明和定义后都加上`const`

|const修饰位置|作用|
|:-|
|变量|变量不可修改,通常用来替代`#define`|
|对象/实例|对象的成员变量不可修改,只能调用`const`成员函数|
|函数参数|参数不能在函数内部修改,只作为入参|
|函数返回值|返回的结果不能被修改,常用于字符串|
|成员变量|只能在初始化列表中初始化|
|成员函数|不改变成员变量|

> 只要能够使用`const`,尽量使用`const`。

#### 9. `static`限定符
本质:
* 生存周期:整个程序的生存周期。
* 作用域:属于类,不属于对象。
语法
* 声明

class 类名{
static 返回类型 函数(形参列表);
};

* 定义

返回类型 类名::函数(形参列表){
函数体;
}

* 调用

类名::函数(实参列表);


* 规则
 * `static`只能用于类的声明中,定义不能标示为`static`。
 * 非静态是可以访问静态的方法和函数
 * 静态成员函数可以设置`private`,`public`,`protected`访问权限

* 禁忌
 * 静态成员函数不能访问非静态函数或者变量
 * 静态成员函数不能使用`this`关键字
 * 静态成员函数不能使用cv限定符(`const`与`virtual`)
> 因为静态成员函数是属于类而不是某个对象。

静态成员变量
语法:
* 在类定义中声明,但是在类实现中初始化。
* 在声明时需要指定关键字`static`,但是实现时不需要指定`static`。
* 对象的大小不包含静态成员变量

> 因为静态成员变量是属于类而不是某个对象。静态成员变量所有类的对象/实例共享。

|`static`修饰位置|作用|
|:-|
|变量|静态变量|
|函数|只源文件内部使用的函数|
|成员变量|对象共享变量|
|成员函数|类提供的函数,或者作为静态成员对象的接口|

> 单例模式:使用静态成员变量和静态成员函数。

---

#### 10. `const static`限定符

include

using std::cout;
using std::endl;

class StaticConstTest{
public:
void print(){
cout << test1 << ” ” << test2 << endl;
}
private:
static const int test1 = 1; // OK
static const int test2; // OK
static const char* test3 = “abcd”;// 出错
static const char* test4;// OK
};

/* static */ const int StaticConstTest::test2 = 2;// OK
/* static / const char StaticConstTest::test4 = “abcd”;// OK
int main(){
StaticConstTest sct;
sct.print();
}


|变量类型|声明位置|
|:-|
|一般成员变量|在构造函数初始化列表中初始化|
|`const`成员常量|必须在构造函数初始化列表中初始化|
|`static`成员变量|必须在类外初始化|
|`static const`/`const static`成员变量|变量声明处或者类外初始化|

> 注意:`static const`/`const static`在类定义中成员变量只能初始化整型数据。

#### 11. 函数的调用优化

`inline` -- 宏定义的接班人

* 条件
一般用在代码比较简单的函数
* 语法
 * 关键字`inline`必须与函数实现/定义体放在一起才能使函数成为内联,将`inline`放在函数声明前面不起任何作用
 * 定义在类声明之中的成员函数将自动地成为内联函数
* 慎用内联
   如果函数体内的代码比较长,使用内联将导致内存消耗代价较高
   如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大
   不要随便地将构造函数和析构函数的定义体放在类声明中
* 本质
  内联函数的代码直接替换函数调用,省去函数调用的开销

#### 12. 运算符重载

案例:实现复数类

* 语法
运算符重载主要有两种方式实现:
 * 成员函数运算符重载

返回值类型 operator 运算符(参数){
函数体
}

 * 友元函数运算符重载

friend 返回值类型 operator 运算符(形参列表) {
函数体
}

* 分类

|No.|类型|运算符|成员函数|友元函数|
|:-|
|1|双目算术运算符|`+` `-` `*` `/` `%`|`类名 operator 运算符(const 类名&) const`|`类名 operator 运算符(const 类名&, const 类名&)`|
|2|关系运算符|`==` `!=` `>` `>=` `<` `<=`|`bool operator 运算符 (const 类名& ) const`|`bool operator 运算符 (const 类名&,const 类名&)`|
|3|双目逻辑运算符|`&&` ¦` |`bool operator 运算符 (const 类名& ) const`|`bool operator 运算符 (const 类名&,const 类名&)`|
|4|单目逻辑运算符|`!`|`bool operator !() const`|`bool operator ! (const 类名&)`|
|5|单目算术运算符|`+` `-`|`类名& operator 运算符 ()`|`类名& operator 运算符 (const 类名&)`|
|6|双目位运算符|`&` `|`类名 operator 运算符 (const 类名& ) const`|`类名 operator 运算符 (const 类名& ,const 类名& )`|
|7|单目位运算符|`~`|`类名 operator ~ ()`|`类名 operator ~ (类名&)`|
|8|位移运算符|`<<` `>>`|`类名 operator 运算符 (int i) const`|`类名 operator 运算符 (const 类名&,int i)`|
|9|前缀自增减运算符|`++` `--`|`类名& operator 操作符 ()`|`类名& operator 操作符 (类名&)`|
|10|后缀自增减运算符|`++` `--`|`类名 operator 操作符 (int)`|`类名 operator 操作符 (类名&,int)`|
|11|复合赋值运算符|`+=` `-=` `*=` `/=` `%=` `&=` =` `^=` |`类名& operator 运算符 (const 类名& )`|`类名& operator += (类名&,const 类名&)`|
|12|内存运算符|`new` `delete`|参见说明|参见说明|
|13|流运算符|`>>` `<<`|-|参见说明|
|14|类型转换符|`数据类型`|参见说明|-|
|15|其他运算符重载|`=` `[]` `()` `->`|参见说明|-|

* 说明
 * 内存运算符比较复杂:
成员函数

void *operator new(size_t size);
void *operator new[](size_t size);
void operator delete(void*p);
void operator delete [](void* p);

友元函数

void *operator new(类名,size_t size);
void *operator new[](类名&,size_t size);
void operator delete(类名&,void*p);
void operator delete [](类名&,void* p);

  * 流运算符
流运算符只能使用友元函数实现。

inline ostream &operator << (ostream&, 类名&)
inline istream &operator >> (istream&, 类名&)

 * 类型转换符
这些运算符只能使用成员函数实现。

operator char* () const;
operator int ();
operator const char () const;
operator short int () const;
operator long long () const;

 * 其他运算符重载
这些运算符只能使用成员函数实现。

类名& operator = (const 类名& );
char operator [] (int i);//返回值不能作为左值
const char* operator () ();
T operator -> ();
“`

  • 规则
    • 五个不能重载的运算符:成员运算符.、指针运算符*、作用域运算符::sizeof、条件运算符?:
    • 不允许用户自定义新的运算符,只能对已有的运算符进行重载
    • 重载运算符不允许改变运算符原操作数的个数
    • 重载运算符不能改变运算符的优先级
    • 重载运算符函数不能有默认的参数,会导致参数个数不匹配
  • 常用的运算符重载

    • =
    • ==
    • () 仿函数
  • 本质
    函数重载

  • 分类

  • 修改自身操作数的运算符

    • 前缀自增减运算符(++ --)
    • 后缀自增减运算符(++ --)返回临时变量
    • 赋值运算符(=)
    • 复合赋值运算符(+= -= *= /= %= &= ¦= ^=)
    • 括号运算符(())
  • 不修改自身操作数的运算符(返回临时变量)
    • 双目算术运算符(+ - * / %)
    • 单目算术运算符(+ -)
    • 关系运算符(== != > >= < <=)
    • 双目逻辑运算符(&& ¦¦)/单目逻辑运算符(!)
    • 双目位运算符(& ¦)
    • 单目位运算符(~)
    • 位移运算符(<< >>)
    • 流运算符(>> <<)
    • 类型转换符(数据类型)
    • 其他运算符重载(* [] ->)

测试时,注意测试操作数对象是否被修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值