C++学习笔记(十:类与对象基础)

发布于:2025-07-27 ⋅ 阅读:(15) ⋅ 点赞:(0)

往篇内容:

 C++学习笔记(一)
        一、C++编译阶段※

        二、入门案例解析 

        三、命名空间详解

        四、C++程序结构

C++学习笔记(二)

        五、函数基础

        六、标识符

        七、数据类型

        补充:二进制相关的概念

                  sizeof 运算符简介

        补充:原码、反码和补码

C++学习笔记(三)

        补充:ASCII码表

        八、内存构成

         补充:变量

        九、指针基础 

        十、const关键字

        十一、枚举类型 

C++学习笔记(四)

        十二、 类型转换

        十三、define指令 

        十四、typedef关键字

        十五、运算符

        十六、 流程控制

C++学习笔记(六)

        十七、数组

C++学习笔记(七)

        十八、指针

C++学习笔记(八)

        十九、函数进阶

        二十、变量进阶

C++学习笔记(九)

        二十一、结构体

        二十二、联合

目录

二十三、类与对象

1、面向过程

2、面向对象

 3、类

4、对象

5、成员引用

 6、成员函数

 7、inline函数

 8、对象内存

 9、this指针

10、类与结构体的区别 

11、封装特性 


 

二十三、类与对象

1、面向过程

        面向过程程序设计(Procedural-Oriented Programming, POP)是一种以过程为中心的编程思想。它强调的是解决问题的步骤和流程,通过函数(或称为过程)来组织代码。

        在 C++ 中,虽然支持 OOP,但仍然兼容传统的面向过程编程方式,例如使用全局变量、函数等。

思想:先做什么,再做什么,即按照顺序执行一系列操作来完成任务。

特点: 

特性 描述
程序结构 由主函数调用多个函数组成
数据与行为分离 数据和操作数据的函数是分开的
顺序执行为主 强调流程控制,按步骤执行
全局变量 常用于共享数据
函数重用 可以通过函数复用代码逻辑

示例:

#include <iostream>
using namespace std;

void add(int a, int b) {
    cout << "Sum: " << a + b << endl;
}
    int main() {
    int x = 5, y = 10;
    add(x, y); // 调用函数

    return 0;
}

2、面向对象

        面向对象程序设计(Object-Oriented Programming, OOP)是一种以对象为核心的编程思想。它将现实世界中的事物抽象为对象,每个对象包含数据(属性)和对数据的操作(方法)。

        C++ 是一种多范式语言,其中最强大的特性就是对 OOP 的完整支持。

思想:“万物皆对象,程序由一组相互协作的对象构成,每个对象封装了自身的状态和行为

四大核心特性: 

概念 描述
封装(Encapsulation) 将数据和操作封装在一个类中,对外隐藏实现细节
继承(Inheritance) 子类可以继承父类的属性和方法,实现代码复用
多态(Polymorphism) 同一个接口可以有多种实现,运行时决定具体行为
抽象(Abstraction) 提取共性特征,忽略复杂实现细节

示例:

#include <iostream>
using namespace std;

class Rectangle {
private:
    int width, height;

public:
    Rectangle(int w, int h) : width(w), height(h) {}

    int area() {
        return width * height;
    }
};

int main() {
    Rectangle rect(5, 10);
    cout << "Area: " << rect.area() << endl;
    return 0;
}

 3、类

        类(class是用户自定义的数据类型,用于封装数据(属性)和操作这些数据的方法(行为)。它是面向对象编程(OOP)的核心概念之一。

        你可以把类看作是蓝图模板,而对象则是根据这个模板创建出来的具体实例。

定义格式: 

class 类名{

        成员访问限定符: //private,public,protected三种取值

                [数据成员s];

                [成员函数s];

};

 成员访问限定符:用来指定个数据成员的访问属性

  • public :类内类外都可以访问
  • private :只能被本类的成员函数引用,类外不能调用(特殊情况后面再讨)
  • protected :和private类似,派生时情况不同

注意事项:

  • 成员访问限定符可以出现多次
  • 作用范围:从当前限定符到下一个限定符或到类的 }

示例:

class Student {

private:

        string name;

        int age;

public:

        void setName(string n) { name = n; }

        void setAge(int a) { age = a; }

        void printInfo() {

                cout << "Name: " << name << ", Age: " << age << endl;

        }

};

注意事项: 

  • 声明或定义类类型时不分配内存空间,用类类型实例创建对象时,分配内存空间
  • 不指定成员限定符,默认为 private
  • 定义类对象的方式有3种,与结构体类似
  • 类的成员函数只能由类的对象或指针调用(特殊情况后续讨论)

4、对象

  • 客观世界中任何事物都可以看成对象,对象由属性和方法组成,每个对象都具有静态的属性和动态的功能。
  • 具有相同属性和功能的对象,我们可以把它们归为一类,故而我们可以得出结论:类是对象的抽象,对象是类的实例。

抽象概念理解:抽象的过程是将有关事物的共性归纳、集中的过程。

定义对象格式:


class 类名 对象名;

类名 对象名; //推荐

//定义类的同时实例化对象

class 类名{

        数据成员s;

        成员函数s;

}对象1,对象2...对象n;

//定义匿名类,同时实例化对象

class {

        数据成员s;

        成员函数s;

}对象1,对象2...对象n;

案例:

#include <iostream>
#include <cstring>
using namespace std;

// 定义类类型
class Student {
    // 默认权限修饰符为 private
    // 成员函数,一般 public
public:
    void disp() {
        cout << "id: " << id << endl;
        cout << "name: " << name << endl;
        cout << "age: " << age << endl;
    }

    // 权限修饰符,一般数据成员 private
    // private:
public:
    int id;
    char name[20];
    int age;
} s3 = {1020, "lucy", 21}; // 列表初始化依旧有用

int main() {
    // 由类类型实例化对象【定义变量】
    Student s1;
    s1.id = 1001;
    // s1.name = "tom"; error,详见字符串数组
    strcpy(s1.name, "tom");
    s1.age = 20;

    // 类对象调用成员函数
    Student s2 = s1; // 修正:删除了多余的class关键字
    s2.disp();

    cout << "--------------" << endl;
    s3.disp();

    return 0;
}

 注意:对象初始化可以采用列表的方式进行,数据成员不能为private。

5、成员引用

使用类对象调用数据成员或成员函数有2种方式:

  • 对象名.成员名;
  • 类指针变量->成员名;

案例:

#include <iostream>
using namespace std;

// 自定义结构体类型
class Student {
public:
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];

public:
    // 成员函数必须通过类对象或指针来调用
    // 对象名.disp(); 指针变量->disp();
    void disp() {
        cout << "Student: [" << num << "," << name << "," << sex
             << "," << age << "," << score << "," << addr << "]" << endl;
    }
};

// 值传递
void printStudent(Student s) {
    cout << "Student: " << s.num << "," << s.name << "," 
         << s.sex << "," << s.age << "," << s.score << "," << s.addr << endl;
}

// 指针传递
void printStudent(Student *p) {
    cout << "Student: {" << p->num << "," << p->name << "," 
         << p->sex << "," << p->age << "," << p->score << "," << p->addr 
         << "}" << endl;
}

// 引用传递(注意:不能与值传递同名,会产生二义性)
void printStudent2(Student &s) {
    cout << "Student: (" << s.num << "," << s.name << "," 
         << s.sex << "," << s.age << "," << s.score << "," << s.addr << ")"
         << endl;
}

int main() {
    // 初始化
    Student s = {2001, "Tom", 'M', 19, 78.5, "平遥学院路56号"};
    
    printStudent(s);    // 值传递
    printStudent2(s);   // 引用传递
    
    Student *ps = &s;
    printStudent(ps);   // 指针传递
    
    cout << "---------------------" << endl;
    s.disp();           // 成员函数调用
    
    return 0;
}

 6、成员函数

        类的成员函数(member function是类的重要组成部分之一。它们定义了类的行为,即对象可以执行的操作。

        它的用法和作用和之前学习的函数基本上是一样的,也有返回值和函数类型,它与一般函数的区别只是: 它是属于一个类的成员,出现在类体中。

        在使用类函数时,要注意调用它的权限 (它能否被调用 )以及它的作用域 (函数能使用什么范围中的数据和函数 )

成员函数定义方式

① 在类内定义(隐式内联)

class Circle {

private:

        double radius;

public:

        void setRadius(double r) { radius = r; } // 类内定义

        double getArea() { return 3.14159 * radius * radius; }

};

⚠️ 这种方式适合简单函数,编译器会尝试将其优化为内联函数 (inline )。

 ② 在类外定义(推荐)

class Circle {

private:

        double radius;

public:

        void setRadius(double r); // 函数声明

        double getArea();

};

// 类外实现

// 返回值类型 类名::函数名(参数列表);

void Circle::setRadius(double r) {

        radius = r;

}

double Circle::getArea() {

        return 3.14159 * radius * radius;

}

推荐这种方式:代码更清晰、结构更合理,便于维护。 

注意:

如果函数名前面既无类名又无 ,如 display() display() 这表示 display 函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。

③ 成员函数的调用 

成员函数可以通过对象或指针来调用:

Circle c;

c.setRadius(5.0); // 通过对象调用

c.printInfo();

Circle* pc = &c;

pc->setRadius(10.0); // 通过指针调用

④ const成员函数

如果你希望某个成员函数不修改类成员变量的值,那可以将其声明为 const

class Rectangle {

private:

        int width, height;

public:

        int area() const {

                return width * height; // 不允许修改成员变量

}

        //获取周长

        int perimeter() const;

};

//类外定义const函数,一定要跟上const修饰

int Rectangle::perimeter() const

{

        //width = 2; error

        return 2 * (width + height);

}

🔒 注意:const 成员函数只能访问其他 const 成员函数,不能修改任何数据成员。

 7、inline函数

        为了减少时间开销,如果在类体中定义的成员函数中不包括循环等控制结构,C++系统会自动将它们作为内联 inline 函数来处理。

class Circle {

private:

        double radius;

public:

        //类内定义,即使没有inline声明,默认也为inline函数

        void setRadius(double r) { radius = r; }

        double getArea() { return 3.14159 * radius * radius; }

};

在调用 setRadiusgetArea 时,并不真正执行函数调用过程 ,而是将函数代码嵌入程序的调用点,大大减少了调用成员函数的时间开销。

注意1:

如果成员函数在类体外定义**,系统并不把它默认为 inline 函数

注意2

如果要在类体外定义 inline 函数,需要在函数定义前加上 inline ,还应类定义和成员函数的定义都放在同一个头文件中 (或者写在同一个源文件中),否则编译时无法进行代码嵌入。例如:

 //函数定义时加inline,声明时则不需要

inline void CirclesetRadius(double r)

{

        radius = r;

}

弊端:上述做法,不利于类的接口与类的实现分离,不利于信息隐蔽。虽然程序的执行效率提高了,但从软件工程质量的角度来看,这样做并不是好的办法。

 8、对象内存

        用类去定义对象时,系统会为每一个对象分配存储空间。每个对象占用内存空间大小,只取决于数据成员和对齐方式,和成员函数没有任何关系。

案例: 

#include <iostream>
#include <cstddef> // for alignof
using namespace std;

class MyClass {
public:
    int x;
    double y;
    char z;

    void disp() {
        cout << "x: " << x << ", y: " << y << ", z: " << z << endl;
    }
};

int main() {
    cout << "Alignment of MyClass: " << alignof(MyClass) << " bytes" << endl;
    cout << "Size of MyClass: " << sizeof(MyClass) << " bytes" << endl;

    MyClass obj;
    obj.x = 10;
    obj.y = 3.14;
    obj.z = 'A';
    obj.disp();

    MyClass obj2;
    obj2.x = 3;
    obj2.y = 2.5;
    obj2.z = 'x';
    obj2.disp();

    return 0;
}
思考:为什么调用不同对象成员函数时,执行完全相同的一段函数代码,但是执行结果是不相同的?

答:因为成员函数可以访问和操作对象的实例数据(属性)。每个对象的属性值是独立存储的,因此即使函数代码相同,执行时基于不同的属性值会产生不同的结果。

 9、this指针

每个非静态成员函数都有一个隐藏参数: this ,它是一个指向当前对象的指针。

  • this 是指向调用该函数的对象的指针
  • 常用于解决形参与成员变量同名的问题
  • 可以用来返回当前对象(常用于链式调用)

案例:

#include <iostream>
#include <string>  // 补充缺失的头文件
using namespace std;

class MyClass {
public:
    string name;
    int x;
    double y;
    char z;

    void disp() {
        // this指向成员函数的调用者
        cout << "this: " << this << endl;
        cout << "name: " << this->name << ", x: " << this->x;
        cout << ", y: " << this->y << ", z: " << this->z << endl;
    }

    void setName(const string& name) {
        this->name = name;  // 也可以区分同名变量
    }
};

int main() {
    MyClass c1 = {"zs", 10, 3.14, 'M'};
    cout << "&c1: " << &c1 << endl;
    c1.disp();

    cout << "--------------" << endl;

    MyClass c2 = {"jack", 20, 2.4, 'F'};
    cout << "&c2: " << &c2 << endl;
    c2.disp();

    return 0;
}

10、类与结构体的区别 

C++增加了class类型后,仍保留了结构体类型(struct), 它不是简单地继承 C 的结构体,而是使它也具有类的特点,以便于用于面向对象程序设计。用struct声明的结构体类型实际上也就是类,两者区别如下:

  • struct的缺省作用域为public

  • class的缺省作用域为private

11、封装特性 

类背后蕴含的思想是数据抽象和封装,两个重要优点:

  • 避免类内部出现无意的、可能破坏对象状态的用户级错误
  • 随时间推移根据需求改变或缺陷报告来完善类实现,而不需要改变用户级代码

        C++通过类实现数据的封装,将相关的数据与对数据的操作封装到一个类中。

        类里面的数据和成员函数通过成员限定符的修饰,改变了这些成员的访问属性,实现了信息的隐蔽。

        在修改代码时,具体修改某个类,不会对别的类产生影响,方便调试检查错误,这对于大型项目尤为重要。

C++中,封装通常通过以下方式实现:

  • 使用 private protected 访问修饰符来限制对类成员的直接访问
  • 提供公共方法( getter setter )来间接访问或修改私有成员变量

案例:

class BankAccount {
private:
    double balance; // 私有成员变量,外部无法直接访问

public:
    // 构造函数
    BankAccount(double initialBalance) : balance(initialBalance) {}

    // Getter 方法
    double getBalance() const {
        return balance;
    }

    // 存款方法
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    // 取款方法
    bool withdraw(double amount) {
        if (balance >= amount && amount > 0) {
            balance -= amount;
            return true;
        }
        return false;
    } // 修正:将分号移至此处
}; // 修正:类定义结束符

         在这个例子中, balance 变量是私有的,外界不能直接访问它。相反,提供了 getBalance , deposit withdraw 方法作为与 balance 交互的接口。


网站公告

今日签到

点亮在社区的每一天
去签到