cpp_04_类_对象_this指针_常对象_常(成员)函数

1 类

1.1 类的定义

类的作用是抽象事物(抽取事物特征)的规则。

类的外化表现是用户自定义的复合数据类型(包括成员变量、成员函数):

成员变量用于表达事物的属性,成员函数用于表达事物的行为。

类的表现力远比基本类型强大很多。

结构体(类) 和 变量(对象):

cpp 复制代码
// clsbase_pre.cpp 结构体(类) 和 变量(对象)
#include <iostream>
#include <cstring>
using namespace std;
// 类: 抽取事物特征的规则
struct Human {
    int m_age;
    char m_name[256];
};
int main( void ) {
    Human h; // 申请内存空间(对象)
    h.m_age = 22;
    strcpy( h.m_name, "张飞" );
    cout << "姓名: " << h.m_name << ", 年龄: " << h.m_age << endl;
    return 0;
}

1.2 类的一般形式:

cpp 复制代码
// clsbase.cpp 结构体(类) 和 变量(对象)
#include <iostream>
#include <cstring>
using namespace std;
// 类: 抽取事物特征的规则
// struct 
class Human {
public:
    void setinfo( int age=0, const char* name="无名" ) { // 桥梁函数
        if( !strcmp(name, "小二") ) {
            cout << "你才二呢" << endl;
            return;
        }
        m_age = age;
        strcpy( m_name, name ); 
    }
    void getinfo( ) {
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }
private:    
    int m_age;
    char m_name[256];
};
// 以上代码模拟类的设计者(标准库的类,第三方提供的类,自己设计的类)
// ------------------------
// 以下代码模拟类的使用者
int main( void ) {
    Human h; // 定义h (给h分配内存空间)
             // 在h所占据的内存空间中 定义m_age(给m_age分配内存空间),初值随机数
             // 在h所占据的内存空间中 定义m_name(给m_name分配内存空间),初值随机数
    cout << "h的大小:" << sizeof(h) << endl; // 260
    h.setinfo( 22,"张飞" ); 
    h.setinfo( 22,"小二" ); 
    h.getinfo();
//    h.m_age = 22;
//    strcpy( h.m_name, "张飞" );
//    strcpy( h.m_name, "小二" );
//    cout << "姓名: " << h.m_name << ", 年龄: " << h.m_age << endl;
    return 0;
}

1.3 访问控制限定符

public: 公有成员,类自己、子类、外部可以访问--谁都可以访问。

protected: 保护成员,类自己和子类可以访问。

private: 私有成员,类自己可以访问。

在C++中,类(class)和结构(struct)已没有本质性的差别,唯一不同:

类的缺省访问控制限定为私有(private);

结构的缺省访问控制限定为共有(public)。

访问控制限定符仅作用于类,而非作用于对象。

对不同成员的访问控制限定加以区分,体现了C++作为面向对象程序设计语言的封装特性。

类是现实世界的抽象,对象是类在虚拟世界的实例。

2 对象

对象的创建过程如下图。

3 成员函数形参--this

3.1 this指针理论

同一个类的不同对象各自拥有各自独立 的 成员变量。

同一个类的不同对象彼此共享同一份 成员函数。

在成员函数内部,通过this指针,来区分所访问的成员变量隶属于哪个对象。

(除静态成员函数外)类的每个成员函数,都有一个隐藏的指针型形参,名为this

this形参指向调用该成员函数的对象,一般称为this指针。

(除静态成员函数 外)在类的成员函数内部,对所有成员的访问,都是通过this指针进行的。

cpp 复制代码
// this.cpp 成员函数的形参 -- this
#include <iostream>
#include <cstring>
using namespace std;
// 当前程序中有两个对象(h/h2),每个对象中各有一份成员变量(m_age/m_name)共有两份成员变量,
// 成员函数只有一份
class Human {
public:
    void setinfo( /* Human* this */ int age=0, const char* name="无名" ) { // _ZN5Human7setinfoEiPKc
        this->m_age = age;
        strcpy( this->m_name, name ); 
    }
    void getinfo( /* Human* this */ ) { // _ZN5Human7getinfoEv
        cout << "姓名:" << this->m_name << ", 年龄:" << this->m_age << endl;
    }
private:    
    int m_age;
    char m_name[256];
};
// 以上代码模拟类的设计者(标准库的类,第三方提供的类,自己设计的类)
// ------------------------
// 以下代码模拟类的使用者
int main( void ) {
    Human h; // 定义h (给h分配内存空间)
             // 在h所占据的内存空间中 定义m_age(给m_age分配内存空间),初值随机数
             // 在h所占据的内存空间中 定义m_name(给m_name分配内存空间),初值随机数
    cout << "h的大小:" << sizeof(h) << endl; // 260
    h.setinfo( 22,"zhangfei" ); // _ZN5Human7setinfoEiPKc( &h, 22, "zhangfei" )
    h.getinfo(); // _ZN5Human7getinfoEv( &h )

    Human h2; // 定义h2 (给h2分配内存空间)
              // 在h2所占据的内存空间中 定义m_age(给m_age分配内存空间),初值随机数
              // 在h2所占据的内存空间中 定义m_name(给m_name分配内存空间),初值随机数
    cout << "h2的大小:" << sizeof(h2) << endl;
    h2.setinfo( 20, "zhaoyun" ); // _ZN5Human7setinfoEiPKc( &h2, 20, "zhaoyun")
    h2.getinfo(); // _ZN5Human7getinfoEv( &h2 )
    return 0;
}

3.2 this指针的应用

1)有时为了方便,将类的成员变量与该类成员函数的参数取相同标识符(不好的编程习惯),这时在成员函数内部,必须用this指针将二者加以区分。

2)返回基于this指针的自引用,以支持串联调用。

多数情况下,程序员并不需要显示地使用this指针。

cpp 复制代码
// hastothis.cpp 必须自己写this的情况
#include <iostream>
using namespace std;
class Integer {
public:
    void setinfo( /* Integer* this */ int i ) {
        this->i = i; // (1)必须自己写this
    }
    void getinfo( /* Integer* this */ ) {
        cout << /*this->*/i << endl; // 编译器补this
    }
    Integer& increment( /* Integer* this */ ) {
        ++/*this->*/i; // 编译器补this

        return *this; // 返回基于this指针的自引用 (2)必须自己写this
    }
private:
    int i; 
};
// 以上代码模拟类的设计者(标准库的类,第三方提供的类,自己设计的类)
// ------------------------
// 以下代码模拟类的使用者
int main( void ) {
    Integer ix;
    ix.setinfo(1000);
    ix.getinfo();
    ix.increment().increment().increment(); // 串联调用
    ix.getinfo();
    return 0;
}

4 常对象和常函数

4.1 常对象

被const关键字修饰的对象、对象指针、对象引用,统称为常对象:

const User user;

const User* cptr = &user;

const User& cref = user;

(1)在定义常对象时必须初始化;

(2)常对象的成员属性不能进行更新;

(3)常对象不能调用该对象中非常成员函数【非const函数】,否则系统会报错误。

目的:

防止非const成员函数修改常对象中的成员属性的值,因为const成员函数是不

可以修改对象中成员属性的值。

(4)常对象的主要作用是【防止对常对象的成员属性的值进行修改】。

4.2 常(成员)函数

常函数 全称为 常成员函数。

普通成员函数才可能有常属性,变为常成员函数,即常函数。

在类成员函数的形参表之后,函数体之前加上const关键字,则该成员函数的this指针即具有常属性,这样的成员函数被称为常函数:

class 类名 {

返回类型 函数名 (形参表) const {

函数体;

}

};

原型相同的成员函数,常版本和非常版本构成重载

非常对象优先选择非常版本,如果没有非常版本,也能选择常版本;

常对象只能选择常版本。

在常函数内部无法修改成员变量的值,除非该成员变量被mutable关键字修饰。(编译器会做去常转换,见下列代码。)

cpp 复制代码
// usethis.cpp
// 常对象(被const修饰的对象、指针、引用) 和 非常对象(没有被const修饰的对象、指针、引用)
// 常函数(编译器补的this参数有const修饰) 和 非常函数(编译器补的this参数没有const修饰)
#include <iostream>
using namespace std;
class Integer {
public:
    void setinfo( /* Integer* this */ int i ) { // 非常函数
        m_i = i; 
    }
    void getinfo( /* Integer* this */ ) { // 非常函数
        cout << "非常函数getinfo: " << m_i << endl; 
    }
    void getinfo( /* const Integer* this */ ) const { // 常函数
        const_cast<Integer*>(this)->m_i = 8888;
        cout << "常函数getinfo: " << m_i << endl;
    }
private:
    /*mutable*/ int m_i; 
};
// 以上代码模拟类的设计者(标准库的类,第三方提供的类,自己设计的类)
// ------------------------
// 以下代码模拟类的使用者
int main( void ) {
    Integer ix; // ix是非常对象
    ix.setinfo(1000);
    ix.getinfo(); // getinfo(&ix)-->实参为Integer*  非常对象优先选择非常函数,也可选择常函数

    const Integer cix = ix; // cix是常对象
    cix.getinfo(); // getinfo(&cix)-->实参为const Integer* 常对象只能选择常函数,不能选择非常函数

    Integer* pix = &ix; // pix是非常对象
    Integer& rix = ix; // rix是非常对象
    pix->getinfo(); // getinfo(pix)-->实参为Integer*
    rix.getinfo();  // getinfo(&rix)-->实参为Integer*

    const Integer* pcix = &cix; // pcix是常对象
    const Integer& rcix = cix; // rcix是对象
    pcix->getinfo(); // getinfo(pcix)-->实参为const Integer*
    rcix.getinfo(); // getinfo(&rcix)-->实参为const Integer*
    return 0;
}

注意:

**1)普通成员函数才有常函数。**C++中构造函数,静态成员函数,析构函数,全局成员函数都不能是常成员函数。

>构造成员函数的用途是对对象初始化,成员函数主要是用来被对象调用的,如果构造

函数被设置成const,就不能更改成员变量,失去了其作为构造函数的意义。

>同理析构函数要释放成员所以也不能声明常函数。

>全局成员函数和静态成员函数static其函数体内部没有this指针,所以也不能是常成员

函数。

2)常函数中的this指针是常指针,不能在常成员函数中通过this指针修改成员变量的值

3)非const对象可以调用常函数,也能调用非常函数。但是常对象只能调用常函数,不能调用非常函数(常对象也包括常指针和常引用)。

4)函数名和形参表相同的常函数和非常函数构成重载关系,此时,常对象调用常函数,非常对象调用非常函数。

练习题答案:

cpp 复制代码
// TwoDimensional.cpp 设计一个二维坐标系的 类
#include <iostream>
using namespace std;

class TwoDimensional {
public:
    void setinfo( int x, int y ) {
        m_x = x;
        m_y = y;
    }
    void getinfo( /* TwoDimensional* this */ ) { // 非常函数
        cout << "横坐标: " << m_x << ", 纵坐标: " << m_y << endl;
    }
    void getinfo( /* const TwoDimensional* this */ ) const { // 常函数
        cout << "横坐标: " << m_x << ", 纵坐标: " << m_y << endl;
    }
private:
    int m_x; // 横坐标
    int m_y; // 纵坐标
};

int main( void ) {
    TwoDimensional a; // 非常对象
    a.setinfo( 100, 300 );
    a.getinfo( );

    const TwoDimensional ca = a; // ca是常对象
    ca.getinfo( );
    return 0;
}
相关推荐
Smile丶凉轩5 分钟前
微服务即时通讯系统的实现(服务端)----(1)
c++·git·微服务·github
萝卜兽编程28 分钟前
优先级队列
c++·算法
珹洺2 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
孙同学要努力2 小时前
C++知识整理day1——前置基础知识整理(命名空间、输入输出、函数重载、引用)
开发语言·c++
沐泽Mu2 小时前
嵌入式学习-C嘎嘎-Day05
开发语言·c++·学习
几窗花鸢2 小时前
力扣面试经典 150(下)
数据结构·c++·算法·leetcode
Beau_Will2 小时前
数据结构-树状数组专题(1)
数据结构·c++·算法
hunandede3 小时前
av_image_get_buffer_size 和 av_image_fill_arrays
c++
怀澈1224 小时前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming19875 小时前
STL关联式容器之set
开发语言·c++