#include <iomanip>
#include <ios>
#include <iostream>
#include<string>
// write your code here......
#include<vector>

using namespace std;

class Employee {
    private:
        string name;
        double salary;
    // write your code here......
    public:
        Employee(string name, double salary) : name(name), salary(salary){}
        string getName() const { 
            return name; }
        double getSalary() const { 
            return salary; }
        double show(){
            const int number = 3500;
            int mouth_s = salary - number;
            double person_s = 0.0;
            if(mouth_s>80000){
                person_s = mouth_s*0.45-13505;
            }else if (mouth_s<=80000 && mouth_s>55000) {
                 person_s = mouth_s*0.35-5505;
            }else if (mouth_s<=55000 && mouth_s>35000) {
                 person_s = mouth_s*0.30-2755;
            }else if (mouth_s<=35000 && mouth_s>9000) {
                 person_s = mouth_s*0.25-1005;
            }else if (mouth_s<=9000 && mouth_s>4500) {
                 person_s = mouth_s*0.20-555;
            }else if (mouth_s<=4500 && mouth_s>1500) {
                 person_s = mouth_s*0.10-105;
            }else if(mouth_s<=1500 && mouth_s>0){
                person_s = mouth_s*0.03;
            }
            return person_s;

        }
    

};

int main() {

    // write your code here......
    vector<Employee*> ve_p;
    ve_p.push_back(new Employee("王五", 100000));
    ve_p.push_back(new Employee("李四", 8000));
    ve_p.push_back(new Employee("张三", 6500));
   
    
    for(const auto& p : ve_p ){
        cout<<p->getName()<<"应该缴纳的个人所得税是:"<<fixed<<setprecision(1)<<p->show()<<endl;
    }

    return 0;
}

一、getter函数是什么?(通俗解释+代码示例)

getter函数(也叫「访问器函数」)是类的公有成员函数,核心作用是:让外部代码安全地读取类的私有/保护属性值(因为私有属性外部无法直接访问)。

简单说:把类的属性“锁起来”(设为private),只留一个「只读小门」(getter函数),外部想读属性值,必须走这个小门。

直观对比:无getter vs 有getter

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

// 示例类:Student(包含name/score两个属性)
class Student {
private:
    // 1. 属性私有化(核心:外部不能直接访问)
    string name;
    int score;

public:
    // 构造函数:初始化属性
    Student(string n, int s) : name(n), score(s) {}

    // 2. getter函数:读取私有属性(命名规范:get+属性名,首字母大写)
    string getName() const { // 读取name,const保证函数不修改类成员
        return name;
    }

    int getScore() const { // 读取score
        return score;
    }
};

int main() {
    Student stu("张三", 85);

    // ❌ 错误:私有属性外部无法直接访问
    // cout << stu.name << endl; 

    // ✅ 正确:通过getter函数读取属性
    cout << "姓名:" << stu.getName() << endl;  // 输出:张三
    cout << "分数:" << stu.getScore() << endl; // 输出:85

    return 0;
}

二、为什么需要getter函数?(核心:封装+安全+可维护)

如果直接把属性设为public(外部能直接读写),虽然写代码快,但会导致数据混乱、代码失控、后期维护难——这违背了面向对象的核心思想「封装」(把类的内部数据和逻辑隐藏,只暴露必要的接口)。

getter函数的核心价值有5点,结合例子讲清楚:

1. 保护数据安全:避免属性被非法篡改/读取

如果属性公有,外部可以随便改,很容易出现不符合逻辑的值(比如分数设为-100);而私有化+getter能严格控制访问:

// 反例:属性公有,数据混乱
class BadStudent {
public:
    string name;
    int score;
};

int main() {
    BadStudent stu("李四", 90);
    stu.score = -50; // 非法值!外部可以随便改,类无法阻止
    cout << stu.score << endl; // 输出-50,数据完全失控
    return 0;
}

2. 控制读取规则:可在getter中加逻辑,返回“合法值”

比如分数是小数(double),但想返回整数;或者隐藏敏感信息(比如姓名只显示首字),只需要改getter函数,外部代码不用动:

class Student {
private:
    string name;
    double score; // 分数改为小数

public:
    Student(string n, double s) : name(n), score(s) {}

    // getter加逻辑:分数四舍五入返回整数
    int getScore() const {
        return (int)(score + 0.5); 
    }

    // getter加逻辑:姓名只显示首字
    string getName() const {
        return name.substr(0, 1) + "**"; 
    }
};

int main() {
    Student stu("张三", 85.6);
    cout << stu.getScore() << endl; // 输出86(四舍五入)
    cout << stu.getName() << endl;  // 输出张**(隐藏敏感信息)
    return 0;
}

3. 实现“只读属性”:只给getter,不给setter

如果想让属性“只能读、不能改”(比如学生的入学时间),只提供getter函数,不提供修改属性的setter函数即可:

class Student {
private:
    string enrollTime = "2026-01-01"; // 入学时间(固定)

public:
    // 只有getter,没有setter → 外部只能读,不能改
    string getEnrollTime() const {
        return enrollTime;
    }
};

int main() {
    Student stu;
    cout << stu.getEnrollTime() << endl; // 可读:2026-01-01
    // stu.enrollTime = "2025-12-31"; // ❌ 私有属性,改不了
    // 也没有setEnrollTime函数 → 彻底只读
    return 0;
}

4. 提高代码可维护性:内部修改不影响外部

如果后续需要修改属性的存储方式(比如把scoreint改成double),只需要修改getter函数的返回逻辑,外部调用的代码完全不用动:

// 版本1:score是int
class StudentV1 {
private:
    int score;
public:
    int getScore() const { return score; }
};

// 版本2:score改成double,只需改getter
class StudentV2 {
private:
    double score; // 内部存储变了
public:
    int getScore() const { return (int)score; } // 外部返回值类型不变
};

// 外部调用代码:完全不用改!
void printScore(const StudentV2& stu) {
    cout << stu.getScore() << endl; // 不管内部怎么变,调用方式不变
}

5. 符合面向对象设计原则:高内聚、低耦合

类的内部实现(比如属性类型、存储规则)是“内聚”的,对外只暴露统一的接口(getter),外部代码和类的内部细节解耦——就算类的内部逻辑改烂了,只要接口不变,外部代码就不用改。

三、getter函数的命名规范(行业通用)

为了代码可读性,getter函数有固定命名习惯:

  • 普通属性:get + 属性名(首字母大写),比如getName()getScore()
  • 布尔类型属性:is + 属性名(比如isPass():判断是否及格,代替getPass())。

示例(布尔型getter):

class Student {
private:
    int score;
public:
    Student(int s) : score(s) {}
    // 布尔型:is+属性(是否及格)
    bool isPass() const {
        return score >= 60;
    }
};

int main() {
    Student stu(85);
    if (stu.isPass()) {
        cout << "及格" << endl;
    }
    return 0;
}

四、补充:getter和setter是“搭档”(可选)

和getter配套的是setter函数(修改器函数),作用是“安全地修改私有属性”(比如修改分数时校验合法性):

class Student {
private:
    int score;
public:
    // setter:修改分数,加校验(不能是负数)
    void setScore(int s) {
        if (s >= 0 && s <= 100) { // 合法范围:0~100
            score = s;
        } else {
            cout << "分数非法!" << endl;
        }
    }

    // getter:读取分数
    int getScore() const {
        return score;
    }
};

int main() {
    Student stu;
    stu.setScore(90);  // 合法:score=90
    stu.setScore(-50); // 非法:提示“分数非法”,score不变
    cout << stu.getScore() << endl; // 输出90
    return 0;
}

总结

外部可直接改属性,数据易混乱

外部只能通过getter读,类控制规则

内部改属性逻辑,外部代码全要改

内部改逻辑,外部调用代码不变

无法实现“只读属性”

只给getter,即可实现只读

违背封装原则

符合面向对象封装思想

getter函数的核心:把类的内部数据“锁起来”,只留一个可控的“读取通道”,既保证数据安全,又提高代码可维护性——这是面向对象编程的基础实践,也是工业级代码的标配。

在C++中,将自定义类的多个实例存入容器(如vector/list/map等,最常用vector)是非常基础且高频的操作,核心步骤是:定义类 → 选择容器类型 → 创建类对象 → 存入容器 → 访问/遍历容器中的类对象

下面以「包含两个属性的Student类」为例,从基础(存储对象本身)进阶(存储指针/智能指针) 完整讲解,所有代码可直接运行。

一、核心前置:定义包含两个属性的类

首先定义一个自定义类(比如Student),包含两个属性(如namescore),并编写构造函数(方便快速创建对象),可选写成员函数(如打印属性)。

#include <iostream>
#include <vector>   // 容器头文件
#include <string>   // 字符串头文件
#include <memory>   // 智能指针头文件(进阶用)
using namespace std;

// 定义自定义类:包含两个属性(name/score)
class Student {
public:
    // 两个核心属性(可设为public,或用get/set封装,新手先public)
    string name;  // 属性1:姓名
    int score;    // 属性2:分数

    // 构造函数:快速初始化对象(必须!否则无法便捷创建实例)
    Student(string n, int s) : name(n), score(s) {}

    // 可选:成员函数,打印属性(方便后续遍历输出)
    void showInfo() const {
        cout << "姓名:" << name << ",分数:" << score << endl;
    }
};

二、基础版:容器存储「类对象本身」(值语义)

最直观的方式:容器类型为vector<Student>,直接存储类的实例(对象),适合对象体积小、无需多态的场景。

实现步骤:

  1. 创建vector<Student>类型的容器;
  2. emplace_back(推荐)或push_back添加类对象;
  3. 遍历容器,访问对象的属性/调用成员函数。

完整代码:

int main() {
    // 1. 创建容器:存储Student对象
    vector<Student> stuContainer;

    // 2. 创建多个Student对象,存入容器(两种方式,优先emplace_back)
    // 方式1:emplace_back(推荐):直接在容器内存中构造对象,减少拷贝,效率更高
    stuContainer.emplace_back("张三", 85);  // 传入构造函数参数,直接构造
    stuContainer.emplace_back("李四", 92);
    // 方式2:push_back:先创建临时对象,再拷贝到容器(效率略低)
    stuContainer.push_back(Student("王五", 78));

    // 3. 遍历容器,访问类的属性/调用成员函数
    cout << "=== 遍历容器中的Student对象 ===" << endl;
    // 范围for遍历(const& 避免拷贝,只读更安全)
    for (const auto& stu : stuContainer) {
        // 方式1:直接访问属性(public属性可直接读)
        // cout << stu.name << ":" << stu.score << endl;
        // 方式2:调用成员函数(更封装)
        stu.showInfo();
    }

    // 4. 单独访问某个对象的属性(通过下标)
    cout << "\n第一个学生的分数:" << stuContainer[0].score << endl;

    return 0;
}

输出结果:

=== 遍历容器中的Student对象 ===
姓名:张三,分数:85
姓名:李四,分数:92
姓名:王五,分数:78

第一个学生的分数:85

三、进阶版1:容器存储「类对象指针」(指针语义)

当类对象体积大、需要避免拷贝,或需要多态(比如子类对象存入父类指针容器)时,选择存储指针:容器类型为vector<Student*>

实现步骤:

  1. 创建vector<Student*>容器;
  2. new创建类对象(堆内存),将指针存入容器;
  3. 遍历容器,用->访问属性/调用成员函数;
  4. 必须手动释放内存(否则内存泄漏)。

完整代码:

int main() {
    // 1. 创建容器:存储Student对象的指针
    vector<Student*> stuPtrContainer;

    // 2. new创建对象,指针存入容器(堆内存)
    stuPtrContainer.push_back(new Student("赵六", 90));
    stuPtrContainer.push_back(new Student("钱七", 88));

    // 3. 遍历容器:指针用->访问成员
    cout << "=== 遍历容器中的Student指针 ===" << endl;
    for (auto ptr : stuPtrContainer) {
        ptr->showInfo(); // 指针访问成员:->
        // 也可直接访问属性:cout << ptr->name << ":" << ptr->score << endl;
    }

    // 4. 关键:手动释放堆内存(避免内存泄漏)
    cout << "\n释放内存..." << endl;
    for (auto ptr : stuPtrContainer) {
        delete ptr; // 释放单个对象
    }
    stuPtrContainer.clear(); // 清空容器(可选)

    return 0;
}

输出结果:

=== 遍历容器中的Student指针 ===
姓名:赵六,分数:90
姓名:钱七,分数:88

释放内存...

四、进阶版2:容器存储「智能指针」(现代C++,推荐)

手动释放指针容易遗漏导致内存泄漏,现代C++推荐用智能指针unique_ptr/shared_ptr),自动管理内存,无需手动delete

核心:vector<unique_ptr<Student>>(独占指针,最常用)

unique_ptr保证每个指针唯一指向一个对象,超出作用域自动释放内存。

完整代码:

int main() {
    // 1. 创建容器:存储unique_ptr<Student>(智能指针)
    vector<unique_ptr<Student>> stuSmartContainer;

    // 2. 用make_unique创建智能指针,存入容器(C++14及以上支持)
    stuSmartContainer.push_back(make_unique<Student>("孙八", 76));
    stuSmartContainer.emplace_back(make_unique<Student>("周九", 82));

    // 3. 遍历容器:智能指针用->访问成员
    cout << "=== 遍历容器中的智能指针 ===" << endl;
    for (const auto& ptr : stuSmartContainer) {
        ptr->showInfo();
    }

    // 4. 无需手动释放!unique_ptr自动销毁,内存自动回收

    return 0;
}

输出结果:

=== 遍历容器中的智能指针 ===
姓名:孙八,分数:76
姓名:周九,分数:82

五、关键注意事项(避坑!)

  1. 构造函数必须有:若未写构造函数,编译器会生成默认构造函数,但无法便捷初始化属性;自定义带参数的构造函数后,默认构造函数会被覆盖,需手动添加(如果需要)。
  2. emplace_back vs push_back:emplace_back:直接在容器内存中构造对象,无拷贝,效率更高;push_back:先创建临时对象,再拷贝/移动到容器,适合已有对象的场景; 优先用emplace_back。
  3. 存储对象 vs 存储指针:存储对象无需手动管理内存、简单对象拷贝开销大、不支持多态对象小、无多态需求存储裸指针无拷贝、支持多态需手动释放内存(易泄漏)对象大、需多态存储智能指针无拷贝、支持多态、自动释放C++11+支持、语法稍复杂现代开发首选(替代裸指针)
  4. 访问权限:若类的属性设为private(封装推荐),需编写getter函数访问属性,比如:

六、拓展:多态场景(子类对象存入父类指针容器)

如果有子类(如GraduateStudent继承Student),存储父类指针容器可实现多态:

// 父类:Student(添加虚函数支持多态)
class Student {
public:
    string name;
    int score;
    Student(string n, int s) : name(n), score(s) {}
    // 虚函数:支持多态
    virtual void showInfo() const {
        cout << "本科生:" << name << ",分数:" << score << endl;
    }
};

// 子类:GraduateStudent
class GraduateStudent : public Student {
public:
    string research; // 新增属性:研究方向
    GraduateStudent(string n, int s, string r) : Student(n, s), research(r) {}
    // 重写虚函数
    void showInfo() const override {
        cout << "研究生:" << name << ",分数:" << score << ",研究方向:" << research << endl;
    }
};

int main() {
    // 父类指针容器,可存子类对象(多态)
    vector<unique_ptr<Student>> polyContainer;
    polyContainer.push_back(make_unique<Student>("本科生张三", 85));
    polyContainer.push_back(make_unique<GraduateStudent>("研究生李四", 92, "人工智能"));

    // 遍历:自动调用子类重写的函数(多态)
    for (const auto& ptr : polyContainer) {
        ptr->showInfo();
    }
    return 0;
}

输出结果:

本科生:本科生张三,分数:85
研究生:研究生李四,分数:92,研究方向:人工智能

总结

核心流程:

定义类(带构造函数)选择容器类型(对象/指针/智能指针)创建类实例存入容器遍历/访问类成员

  • 新手优先用「存储对象 + emplace_back」,简单无内存风险;
  • 进阶场景(对象大/多态)用「智能指针容器」,兼顾效率和内存安全;
  • 永远避免裸指针容器的内存泄漏问题(要么手动释放,要么用智能指针)。