C++基础-类与对象

来源:互联网 发布:selected知乎 编辑:程序博客网 时间:2024/06/07 22:40

认识类和对象

  • 面向对象四大特征
    抽象:抽出事物 的最本质的特征;
    封装:把数据和处理(函数)包在一起;
    继承:数据和处理函数的传承;
    多态:同一个事物(函数)的多种形态;

类的定义和创建

类的定义:与C++中的’struct‘类似
class 类 名{    成员变量;    成员函数;};
  • 构成
    数据成员(data member)/成员变量/属性:对象内部数据和状态,只能在类定义中声明,可以在成员函数中直接调用。
    成员函数/方法:对象相关的操作,可以在类内实现或类外实现。

  • 作用域运算符:’::’ 表示函数归属。

  • 访问限制符
    private(默认|私有)
    public(公开)
    protected(保护)

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

  • 声明与实现分离

  • 头文件 - - 声明
    方式:#pragma once或#ifdef ….#endif
    作用:防止头文件二次编译
  • 源文件 - -实现
    引用头文件:#include <>(标准库函数)/#include “”(自定义/第三方函数)

  • ‘class’与’struct’的区别
    1.默认的访问控制不同
    ‘struct’是’public’
    ‘class’是’private’
    2.’struct ‘可以使用花括号内的初始值列表’{…}’初始化,’class’不可以(c++98不可以,C++11可以)。

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

  • 成员变量默认初始化为随机值(主要影响指针)。
对象创建
  • 直接定义 - - 类作为类型定义变量 - - 栈上创建
类名 对象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);//等价于cosnt float b = 1.0;

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

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

’this‘ 指针

  • 作用域 - - 类内部

  • 特点
    1.类的一个自动生成、自动隐藏的私有成员
    2.每个对象仅有一个’this’指针
    3.当一个对象被创建时,‘this’指针就存放指向对象数据的首地址
    4.不是对象本身的一部分,不会影响sizeof(对象)结果

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

方法

构造函数
  • 语法
类名(参数){    函数体}
  • 在对象被创建时自动执行
  • 构造函数的函数名与类名相同
  • 没有返回值类型、也没有返回值

  • 调用时机

    1. 对象直接定义创建 - -构造函数不能被显式调用
    2. ‘new’动态创建
  • 默认构造函数
    类中没有显示的定义任何构造函数,编译器就会自动为该类型生成默认构造函数
  • 构造函数的三个作用
    1. 给创建的对象建立一个标示符
    2. 为对象数据成员开辟内存空间
    3. 完成对象数据成员的初始化
  • 语法
类名(参数):成员变量(参数){    函数体}
  • 作用:初始化非静态成员变量
  • 说明:从概念上来讲,构造函数的执行可以分为两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段
  • 必须使用初始化列表的情况
    1. 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面。
    2. 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面。
    3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
  • 初始化列表与构造函数内部成员变量赋值的区别:成员变量初始化与成员变量赋值
    能使用初始化列表的时候尽量使用初始化列表
  • 成员变量的初始化顺序:成员变量在使用初始化列表时,与构造函数中初始化成员列表的顺序无关,只与定义的成员变量的顺序有关。
    C++的函数可以加默认参数。
    写法:
    1. 默认参数必须写在函数声明中,不能写在函数实现的参数列表中
    2. 默认参数必须写在所有非默认参数的后面
    3. 默认参数可以写任意多个
      使用:
      默认参数可以不用传递值,此时,使用默认值
      默认参数可以传值,此时,使用实参值
析构函数
  • 语法
~类名{    函数体}
  • 析构函数的函数名与类名相同
  • 函数名前必须有一个’~’
  • 没有参数
  • 没有返回值类型、也没有返回值
  • 只能有一个析构函数

  • 调用时机

    1. 对象离开作用域
    2. ‘delete’
  • 默认析构函数:类中没有显式的定义析构函数,编译器就会自动为该类型生成默认析构函数
  • 作用:释放对象所申请占有的资源
引用(别名)
  • 语法
    声明:‘const类型名& 对象名’/‘类型名& 对象名’
    使用:与对象变量、基本类型变量一样
  • 何处使用引用
    1. 函数参数列表
    2. 成员变量 - - 对象初始化时,必须显示初始化的变量
  • 为何使用引用
    1. 避免对象复制
    2. 避免传递空指针
    3. 使用方便

      类型: 基本类型(‘bool’、‘char’、‘int’、‘short’、‘float’)
      复合类型(指针、数组、引用)


自定义类型(‘struct’、‘union’、‘class’)引用作用:取代指针

引用与指针的区别:
1. 指针指向一块内存,它的内容是指内存的地址;引用是某块内存的别名;
2. 引用只能定义时被初始化一次,之后不可变;指针可以改变;
3. 引用不能为空,指针可以为空;
4. 引用使用时无需解引用‘*’,指针需要解引用;
5. ‘sizeof(引用)’的得到的是所指向的变量/对象的大小,而‘sizeof(指针)’得到的是指针本身的大小。

引用常用于两种情形:成员变量和函数的参数列表。
拷贝/复制构造函数
  • 语法:
类名(类名& 形参){    函数体}

或者

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

或者

类名(const 类名 形参){    函数体std:swap();}
  • 调用时机
    1. 一个对象作为函数参数,以值传递的方式传入函数体
    2. 一个对象作为函数返回值,以值从函数返回
    3. 一个对象用作给另外一个对象进行初始化(赋值初始化)
      实践说明:
      gcc自动NRV(named return value)优化,不执行拷贝构造函数,需要在编译命令添加选项禁止‘-fno-elide-constructors’;
      vs会执行拷贝构造函数
      总之,尽量不要返回局部对象。
默认拷贝构造函数
  • 本质:内存拷贝
  • 作用:复制一个已经存在的对象
    如何禁用默认拷贝构造函数?在类定义,添加私有的复制构造函数的声明,但不实现。

赋值运算符重载函数

  • 语法
类名& operator = (const 类名& 形参){    //赋值操作    return *this;}
  • 调用时机:赋值
  • 默认赋值运算符重载函数:内存拷贝
  • 作用:赋值

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

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

深拷贝与浅拷贝

  • 浅拷贝:只拷贝指针地址
  • 深拷贝:重新分配堆内存,拷贝指针指向内容

友元

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

‘const’限定符

  • 本质:不可修改
  • ‘const’与变量/对象
const 类型 变量 = 初始值const 类型 对象
  • 定义时必须初始化
  • 全局作用域声明的’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 成员变量

    1. 不能在类声明中初始化’const’数据成员
    2. ‘const’成员变量只能在类构造函数的初始化列表中初始化

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

  • 声明

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

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

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

只要能够使用’const’,尽量使用’const’。

‘static’限定符

本质
1. 生存周期:整个程序的生存周期;
2. 作用域:属于类,不属于对象.

  • 声明
class 类名{    static 返回类型 函数(形参列表);};
  • 定义
返回类型 类名::函数(形参列表){    函数体;}
  • 调用
类名::函数(实参列表);
  • 规则
    1. ‘static’只能用于类的声明中,定义不能标示为‘static’
    2. 非静态是可以访问静态的方法和函数
    3. 静态成员函数可以设置‘private’、‘public’、‘protected’访问权限
  • 禁忌
    1. 静态成员函数不能访问非静态函数或者变量
    2. 静态成员函数不能使用‘this’关键字
    3. 静态成员函数不能使用cv限定符(’const‘与’virtual‘)

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

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

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


‘const static’限定符

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

注意:‘static const’/‘const static’成员变量必须是整形。

函数的调用优化

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

    运算符重载

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

      返回值类型 operator 运算符(参数){
      函数体
      }
    2. 友元函数运算符重载
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、条件运算符?:
    • 不允许用户自定义新的运算符,只能对已有的运算符进行重载
    • 重载运算符不允许改变运算符原操作数的个数
    • 重载运算符不能改变运算符的优先级
    • 重载运算符函数不能有默认的参数,会导致参数个数不匹配
  • 本质
    函数重载

原创粉丝点击