C++面向对象

面向对象的思想

面向过程:

根据程序的执行过程,来设计软件的所有细节。面向过程的缺点:开发大型项目时,越来越难以把控,甚至失去控制。后期维护、更新成本很大。解决方案:使用面向对象。

什么是面向对象?

不是面向对象,写代码:面向对象是一种开发思想,一种全新的开发方式。面向对象思想的重要性:开发大型项目必备,是高级程序员的必备技能!

类的使用

面向对象编程,最重要的第一个概念:类。"人类"是一个抽象的概念,不是具体的某个人。"类",是看不见,摸不着的,是一个纯粹的概念。"类",是一种特殊的"数据类型",不是一个具体的数据。注意:类, 和基本数据类型(char/int/short/long/long long/float/double)不同。

类的构成:方法和数据

类的设计

对象的 基本 使用

对象,是一个特定"类"的具体实例。对象和普通变量的区别:一般地,一个对象,就是一个特殊的变量,但是有跟丰富的功能和用法。

方式一
cpp 复制代码
#include <iostream>
//#include <Windows.h>
#include <string>

using namespace std;

// 定义一个"人类"
class Human {
public: 
	//公有的,对外的
    void eat(); //方法, "成员函数"
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();

private:
    string name;
    int age;
    int salary;
};

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

    
string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}


int Human::getSalary() {
    return salary;
}

int main(void) {
    Human h1; // 通过自定义的特殊数据类型"Human"类, 来创建一个"对象"
    // 合法使用
    h1.eat();
    h1.play();
    h1.sleep();
    
    // 非法使用
    //cout << "年龄" << h1.age << endl;  //直接访问私有成员,将无法通过编译

    //正确使用
    cout << "年龄" << h1.getAge() << endl; //暴露问题,年龄值是一个很大的负数
    //system("pause");
}

总结:"."的使用、调用方法时,方法名后需要带一对圆括号()、通过对象,只能调用这个对象的public方法

分析:多个不同的对象都有自己的数据,彼此无关。

方式2
cpp 复制代码
int main(void) {
    Human  h1; // 通过自定义的特殊数据类型"Human"类, 来创建一个"对象"
    Human *p;
    p = &h1;
    // 合法使用
    p->eat();
    p->play();
    p->sleep();

    // 非法使用
    //cout << "年龄" << p->age << endl;  //直接访问私有成员,将无法通过编译

    //正确使用
    cout << "年龄" << p->getAge() << endl; //暴露问题,年龄值是一个很大的负数
    system("pause");
}

小结:-> 的使用(类似C语言的结构体用法)、

构造函数

构造函数的作用:在创建一个新的对象时,自动调用的函数,用来进行"初始化"工作:对这个对象内部的数据成员进行初始化。

构造函数的特点:自动调用(在创建新对象时,自动调用)、构造函数的函数名,和类名相同、

  1. 构造函数没有返回类型

  2. 可以有多个构造函数(即函数重载形式)

构造函数的种类

默认构造函数、自定义的构造函数、拷贝构造函数、赋值构造函数

默认构造函数

没有参数的构造函数,称为默认构造函数。

合成的默认构造函数

没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数。如果数据成员使用了"类内初始值",就使用这个值来初始化数据成员【C++11】。否则,就使用默认初始化(实际上,不做任何初始化)。

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;

// 定义一个"人类"
class Human {
public:  //公有的,对外的
    void eat(); //方法, "成员函数"
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
private:
    string name;
    int age = 18;
    int salary;
};

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

int main(void) {
    Human  h1;  // 使用合成的默认初始化构造函数
    cout << "年龄: " << h1.getAge() << endl;     //使用了类内初始值
    cout << "薪资:" << h1.getSalary() << endl;  //没有类内初始值
    system("pause");
    return 0;
}

注意:只要手动定义了任何一个构造函数,编译器就不会生成"合成的默认构造函数"。一般情况下,都应该定义自己的构造函数,不要使用"合成的默认构造函数"【仅当数据成员全部使用了"类内初始值",才宜使用"合成的默认构造函数"】。

手动定义的默认构造函数

常称为"默认构造函数"

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;

// 定义一个"人类"
class Human {
public:  //公有的,对外的
    Human(); //手动定义的"默认构造函数"
    void eat(); //方法, "成员函数"
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

int main(void) {
    Human  h1;  // 使用自定义的默认构造函数
    cout << "姓名:" << h1.getName() << endl;
    cout << "年龄: " << h1.getAge() << endl;    
    cout << "薪资:" << h1.getSalary() << endl;
    system("pause");
    return 0;
}

说明:如果某数据成员使用类内初始值,同时又在构造函数中进行了初始化,那么以构造函数中的初始化为准。相当于构造函数中的初始化,会覆盖对应的类内初始值。

自定义的重载构造函数

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;

// 定义一个"人类"
class Human {
public:  
    Human();
    Human(int age, int salary);
    void eat();
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl;
    this->age = age;      //this是一个特殊的指针,指向这个对象本身
    this->salary = salary;
    name = "无名";
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

int main(void) {
    Human  h1(25, 35000);  // 使用自定义的默认构造函数
    cout << "姓名:" << h1.getName() << endl;
    cout << "年龄: " << h1.getAge() << endl;    
    cout << "薪资:" << h1.getSalary() << endl;
    system("pause");
    return 0;
}

拷贝构造函数

手动定义的拷贝构造函数
cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;
// 定义一个"人类"
class Human {
public:  
    Human();
    Human(int age, int salary);
    Human(const Human&);
    void eat();
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl;
    this->age = age;      //this是一个特殊的指针,指向这个对象本身
    this->salary = salary;
    name = "无名";
}

Human::Human(const Human& man) {
    cout << "调用自定义的拷贝构造函数" << endl;
    name = man.name;
    age = man.age;
    salary = man.salary;
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

int main(void) {
    Human  h1(25, 35000);  // 使用自定义的默认构造函数
    Human  h2(h1);  // 使用自定义的拷贝构造函数
    cout << "姓名:" << h2.getName() << endl;
    cout << "年龄: " << h2.getAge() << endl;    
    cout << "薪资:" << h2.getSalary() << endl;
    system("pause");
    return 0;
}
合成的拷贝构造函数
cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>
using namespace std;
// 定义一个"人类"
class Human {
public:  
    Human();
    Human(int age, int salary);
    //Human(const Human&);  //不定义拷贝构造函数,编译器会生成"合成的拷贝构造函数"
    void eat();
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
    void setAddr(const char *newAddr);
    const char* getAddr();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
    char *addr;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl;
    this->age = age;      //this是一个特殊的指针,指向这个对象本身
    this->salary = salary;
    name = "无名";
    addr = new char[64];
    strcpy_s(addr, 64, "China");
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

void Human::setAddr(const char *newAddr) {
    if (!newAddr) {
        return;
    }
    strcpy_s(addr, 64,  newAddr);
}

const char* Human::getAddr() {
    return addr;
}

int main(void) {
    Human  h1(25, 35000);  // 使用自定义的默认构造函数
    Human  h2(h1);  // 使用自定义的拷贝构造函数
    cout << "h1 addr:" << h1.getAddr() << endl;
    cout << "h2 addr:" << h2.getAddr() << endl;
    h1.setAddr("长沙");
    cout << "h1 addr:" << h1.getAddr() << endl;
    cout << "h2 addr:" << h2.getAddr() << endl;
    system("pause");
    return 0;
}

说明:合成的拷贝构造函数的缺点: 使用"浅拷贝"。解决方案:在自定义的拷贝构造函数中,使用'深拷贝

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个"人类"
class Human {
public:
    Human();
    Human(int age, int salary);
    Human(const Human&);  //不定义拷贝构造函数,编译器会生成"合成的拷贝构造函数"
    void eat();
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
    void setAddr(const char *newAddr);
    const char* getAddr();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
    char *addr;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl;
    this->age = age;      //this是一个特殊的指针,指向这个对象本身
    this->salary = salary;
    name = "无名";
    addr = new char[64];
    strcpy_s(addr, 64, "China");
}

Human::Human(const Human &man) {
    cout << "调用自定义的拷贝构造函数" << endl;
    age = man.age;      //this是一个特殊的指针,指向这个对象本身
    salary = man.salary;
    name = man.name;
    // 深度拷贝
    addr = new char[64];
    strcpy_s(addr, 64, man.addr);
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

void Human::setAddr(const char *newAddr) {
    if (!newAddr) {
        return;
    }
    strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
    return addr;
}

int main(void) {
    Human  h1(25, 35000);  // 使用自定义的默认构造函数
    Human  h2(h1);  // 使用自定义的拷贝构造函数
    cout << "h1 addr:" << h1.getAddr() << endl;
    cout << "h2 addr:" << h2.getAddr() << endl;
    h1.setAddr("长沙");
    cout << "h1 addr:" << h1.getAddr() << endl;
    cout << "h2 addr:" << h2.getAddr() << endl;
    system("pause");
    return 0;
}
什么时候调用拷贝构造函数

调用函数时,实参是对象,形参不是引用类型。如果函数的形参是引用类型,就不会调用拷贝构造函数。函数的返回类型是类,而且不是引用类型,对象数组的初始化列表中,使用对象。

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个"人类"
class Human {
public:
    Human();
    Human(int age, int salary);
    Human(const Human&);  //不定义拷贝构造函数,编译器会生成"合成的拷贝构造函数"
    void eat();
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
    void setAddr(const char *newAddr);
    const char* getAddr();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
    char *addr;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl;
    this->age = age;      //this是一个特殊的指针,指向这个对象本身
    this->salary = salary;
    name = "无名";
    addr = new char[64];
    strcpy_s(addr, 64, "China");
}

Human::Human(const Human &man) {
    cout << "调用自定义的拷贝构造函数" << "参数:" << &man << " 本对象:" << this << endl;
    age = man.age;      //this是一个特殊的指针,指向这个对象本身
    salary = man.salary;
    name = man.name;
    // 深度拷贝
    addr = new char[64];
    strcpy_s(addr, 64, man.addr);
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

void Human::setAddr(const char *newAddr) {
    if (!newAddr) {
        return;
    }
    strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
    return addr;
}

void test(Human man) {
    cout << man.getSalary() << endl;
}

void test2(Human &man) { //不会调用拷贝构造函数,此时没有没有构造新的对象
    cout << man.getSalary() << endl;
}    

Human test3(Human &man) {
    return man;
}

Human& test4(Human &man) {
    return man;
}

int main(void) {
    Human h1(25, 35000);  // 调用默认构造函数
    Human h2(h1);         // 调用拷贝构造函数
    Human h3 = h1;   // 调用拷贝构造函数
    test(h1);       // 调用拷贝构造函数
    test2(h1);   // 不会调用拷贝构造函数
    test3(h1);            // 创建一个临时对象,接收test3函数的返回值,调用1次拷贝构造函数
    Human h4 = test3(h1); // 仅调用1次拷贝构造函数,返回的值直接作为h4的拷贝构造函数的参数
    test4(h1);            // 因为返回的是引用类型,所以不会创建临时对象,不会调用拷贝构造函数
    Human men[] = { h1, h2, h3 }; //调用3次拷贝构造函数
    system("pause");
    return 0;
}

赋值构造函数

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个"人类"
class Human {
public:
    Human();
    Human(int age, int salary);
    Human(const Human&);  //不定义拷贝构造函数,编译器会生成"合成的拷贝构造函数"
    Human& operator=(const Human &);
    void eat();
    void sleep();
    void play();
    void work();
    string getName();
    int getAge();
    int getSalary();
    void setAddr(const char *newAddr);
    const char* getAddr();
private:
    string name = "Unknown";
    int age = 28;
    int salary;
    char *addr;
};

Human::Human() {
    name = "无名氏";
    age = 18;
    salary = 30000;
}

Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl;
    this->age = age;      //this是一个特殊的指针,指向这个对象本身
    this->salary = salary;
    name = "无名";
    addr = new char[64];
    strcpy_s(addr, 64, "China");
}

Human::Human(const Human &man) {
    cout << "调用自定义的拷贝构造函数" << "参数:" << &man << " 本对象:" << this << endl;
    age = man.age;      //this是一个特殊的指针,指向这个对象本身
    salary = man.salary;
    name = man.name;
    // 深度拷贝
    addr = new char[64];
    strcpy_s(addr, 64, man.addr);
}

Human& Human::operator=(const Human &man) {
    cout << "调用" << __FUNCTION__ << endl;
    if (this == &man) {
        return *this; //检测是不是对自己赋值:比如 h1 = h1;
    }
    // 如果有必要,需要先释放自己的资源(动态内存)
    //delete addr;
    //addr = new char[ADDR_LEN];
    // 深拷贝
    strcpy_s(addr, ADDR_LEN, other.addr);
    // 处理其他数据成员
    name = man.name;
    age = man.age;
    salary = man.salary;
    // 返回该对象本身的引用, 以便做链式连续处理,比如 a = b = c;
    return *this;
}

void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}

void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}

void Human::play() {
    cout << "我在唱歌! " << endl;
}

void Human::work() {
    cout << "我在工作..." << endl;
}

string Human::getName() {
    return name;
}

int Human::getAge() {
    return age;
}

int Human::getSalary() {
    return salary;
}

void Human::setAddr(const char *newAddr) {
    if (!newAddr) {
        return;
    }
    strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
    return addr;
}

void test(Human man) {
    cout << man.getSalary() << endl;
}

void test2(Human &man) { //不会调用拷贝构造函数,此时没有没有构造新的对象
    cout << man.getSalary() << endl;
}

Human test3(Human &man) {
    return man;
}

Human& test4(Human &man) {
    return man;
}

int main(void) {
    Human h1(25, 35000);  // 调用默认构造函数
      // 特别注意,此时是创建对象h2并进行初始化,调用的是拷贝构造函数,
    // 不会调用赋值构造函数
    Human h2 = h1;  
    h2 = h1; //调用赋值构造函数
    h2 = test3(h1); //调用赋值构造函数
    Human h3 = test3(h1); //调用拷贝构造函数
    system("pause");
    return 0;
}

如果没有定义赋值构造函数,编译器会自动定义"合成的赋值构造函数",与其他合成的构造函数,是"浅拷贝"(又称为"位拷贝")。

析构函数

作用:对象销毁前,做清理工作。具体的清理工作,一般和构造函数对应。比如:如果在构造函数中,使用new分配了内存,就需在析构函数中用delete释放。如果构造函数中没有申请资源(主要是内存资源),那么很少使用析构函数。

函数名:~类型 没有返回值,没有参数,最多只能有一个析构函数

访问权限:一般都使用public

使用方法:不能主动调用。对象销毁时,自动调用。如果不定义,编译器会自动生成一个析构函数(什么也不做)

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <iostream> #include <Windows.h> #include <string> #include <string.h> using namespace std; // 定义一个"人类" class Human { public: Human(); Human(int age, int salary); Human(const Human&); //不定义拷贝构造函数,编译器会生成"合成的拷贝构造函数" Human& operator=(const Human &); ~Human(); //析构函数 ...... private: string name = "Unknown"; int age = 28; int salary; char *addr; }; Human::Human() { name = "无名氏"; age = 18; salary = 30000; addr = new char[64]; strcpy_s(addr, 64, "China"); cout << "调用默认构造函数-" << this << endl; } ...... Human::~Human() { cout << "调用析构函数-" << this << endl; //用于打印测试信息 delete addr; } void test() { Human h1; { Human h2; } cout << "test()结束" << endl; } int main(void) { test(); system("pause"); return 0; } |

this指针:永不迷失的真爱

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Human::Human(int age, int salary) { cout << "调用自定义的构造函数" << endl; this->age = age; //this是一个特殊的指针,指向这个对象本身 this->salary = salary; name = "无名"; addr = new char[64]; strcpy_s(addr, 64, "China"); } |

说明:在类的静态成员函数【后续学习】中,不能使用this指针!

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <iostream> #include <Windows.h> #include <string> #include <string.h> using namespace std; // 定义一个"人类" class Human { public: Human(); Human(int age, int salary); ...... int getAge() const; const Human* compare1(const Human *); private: string name = "Unknown"; int age = 28; int salary; char *addr; }; int Human::getAge() const { return age; } const Human* Human::compare1(const Human * other) { if (age > other->age) { return this; //没有创建新的对象 } else { return other; } } int main(void) { Human h1(25, 30000); Human h2(18, 8000); cout << h1.compare1(&h2)->getAge() << endl; system("pause"); return 0; } |

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ...... class Human { public: Human(); Human(int age, int salary); int getAge() const; const Human* compare1(const Human *); const Human& compare2(const Human&); private: string name = "Unknown"; int age = 28; int salary; char *addr; }; ...... const Human& Human::compare2(const Human& other) { if (age > other.age) { return *this; //访问该对象本身的引用,而不是创建一个新的对象 } else { return other; } } int main(void) { Human h1(25, 30000); Human h2(18, 8000); cout << h1.compare2(h2).getAge() << endl; system("pause"); return 0; } |

this不能指向其他对象,堪称"永不迷失的真爱"

|-----------------------------------------------------------------------------------------------------------------------------------------|
| class Human { public: Human(); Human(int age, int salary); ...... void thisTestError(Human *other) { this = other; // 将报错! } ...... }; |

类文件的分离

实际开发中,类的定义保存在头文件中,比如Human.h【类的声明文件】(C++PrimerPlus),类的成员函数的具体实现,保存在.cpp文件中,比如Human.cpp【类的方法文件】(C++PrimerPlus)。其他文件,如果需要使用这个类,就包含这个类的头文件。

静态 数据 成员:大众情人

需求分析:需要获取总的人数,如何实现?只能使用一个全局变量,然后在构造函数中对这个全局变量进行修改(加1)缺点:使用全局变量不方便,破坏程序的封装性。

解决方案:使用类的静态成员。

定义:Human.h

|------------------------------------------------------------------------------------------------------------------------------------|
| class Human { public: ...... int getCount(); private: string name = "Unknown"; int age = 28; ...... // 类的静态成员 static int count; }; |

初始化:Human.cpp

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include "Human.h" // 初始化类的静态成员 int Human::count = 0; ...... Human::Human() { cout << "调用构造函数:" << this << endl; name = "无名氏"; age = 18; salary = 30000; addr = new char[ADDR_LEN]; strcpy_s(addr, ADDR_LEN, "China"); count++; } // 类的普通成员函数,可以直接访问静态成员(可读可写) int Human::getCount() { return count; } |

main.cpp

|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include "Human.h" int main(void) { Human h1; cout << h1.getCount() << endl; Human h2; cout << h1.getCount() << endl; system("pause"); return 0; } |

对于非const的类静态成员,只能在类的实现文件中初始化。const类静态成员,可以在类内设置初始值,也可以在类的实现文件中设置初始值。(但是不要同时在这两个地方初始化,只能初始化1次)。

静态成员函数:不能拥有的方法

上一节getCount的讨论:当需要获取总的人数时,还必须通过一个对象来访问,比如h1.getCount().如果当前没有可用的对象时,就非常尴尬,不能访问getCount()!

|----------------------------------------------------------------|
| void test() { cout << "总人数: "; // ??? 没有可用的对象来访问getCount() } |

如果为了访问总的人数,而特意去创建一个对象,就很不方便,而且得到的总人数还不真实(包含了一个没有实际用处的人)。解决方案:把getCount()方法定义为类的静态方法!

类的静态方法:可以直接通过类来访问【更常用】,也可以通过对象(实例)来访问。在类的静态方法中,不能访问普通数据成员和普通成员函数(对象的数据成员和成员函数)

Human.h

|-----------------------------------------------------------------------------------|
| #pragma once ...... class Human { public: ...... static int getCount(); ...... }; |

Human.cpp

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ...... //静态方法的实现,不能加static int Human::getCount() { // 静态方法中,不能访问实例成员(普通的数据成员) // cout << age; // 静态方法中,不能访问this指针 // 因为this指针是属于实例对象的 // cout << this; //静态方法中,只能访问静态数据成员 return count; } ...... |

main.cpp

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| void test() { cout << "总人数: "; // ??? 没有可用的对象来访问getCount() // 直接通过类名来访问静态方法! // 用法:类名::静态方法 cout << Human::getCount(); } int main(void) { Human h1, h2; test(); system("pause"); return 0; } |

说明:

静态数据成员

对象的成员函数(没有static的成员函数)内部,可以直接访问"静态数据成员"。类的静态成员函数(有static的成员函数)内部,可以直接访问"静态数据成员"。即:所有的成员函数,都可以访问静态数据成员。类不能直接访问普通的静态数据成员(Human::humanCount 非法)

静态成员函数

对象可以直接访问静态成员函数。类可以直接访问静态成员函数(Human::getHumanCount())。在类的静态成员函数(类的静态方法)内部,不能直接访问this指针和对象的数据成员!在类的静态成员函数(类的静态方法)内部,只能访问类的数据成员

const数据成员

需求分析:怎样表示人的"血型"?血型可以修改吗?

解决方案:把血型定义为const数据类型(常量数据成员)

const数据成员的初始化方式:使用类内值(C++11支持)、使用构造函数的初始化列表(如果同时使用这两种方式,以初始化列表中的值为最终初始化结果)

注意: 不能在构造函数或其他成员函数内,对const成员赋值!

Human.h

|---------------------------------------------------------------------------------------------|
| #pragma once ...... class Human { public: ...... private: ...... const string bloodType; }; |

Human.cpp

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| // 使用初始化列表,对const数据成员初始化 Human::Human():bloodType("未知") { ...... //在成员函数内,不能对const数据成员赋值 //bloodType = "未知血型"; count++; } void Human::description() const { cout << "age:" << age << " name:" << name << " salary:" << salary << " addr:" << addr << " bloodType:" << bloodType << endl; //其他成员函数可以"读"const变量 } |

Main.cpp

|---------------------------------------------------------------------------|
| int main(void) { Human h1; h1.description(); system("pause"); return 0; } |

const成员函数

需求分析:const的Human对象,不能调用普通的成员函数。

分析:C++认为,const(常量)对象,如果允许去调用普通的成员函数,而这个成员函数内部可能会修改这个对象的数据成员!而这讲导致const对象不再是const对象!

【类比】:专一男就是const对象,撩妹方法,就是普通的成员函数,如果允许专一男调去撩妹,那么专一男,也就不专一了!

解决方案:如果一个成员函数内部,不会修改任何数据成员,就把它定义为const成员函数。

Human的description方法

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| //Human.h class Human { public: ...... void description() const; //注意,const的位置 ...... }; //Human.cpp void Human::description ()const { cout << "age:" << age << " name:" << name << " salary:" << salary << " addr:" << addr << " bloodType:" << bloodType << endl; } //main.cpp int main(void) { const Human h1; h1.description(); system("pause"); return 0; } |

const成员函数内,不能修改任何数据成员!

C++的成员函数设置建议:如果一个对象的成员函数,不会修改任何数据成员,那么就强烈:把这个成员函数,定义为const成员函数!

建模的常用手段:组合与聚合

说明:组合和聚合,不是C++的语法要求,是应用中的常用手段。

组合

需求:构建一个计算机类,一台计算机,由CPU芯片,硬盘,内存等组成。CPU芯片也使用类来表示。

CPU.h

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #pragma once #include <string> class CPU { public: CPU(const char *brand = "intel", const char *version="i5"); ~CPU(); private: std::string brand; //品牌 std::string version; //型号 }; |

CPU.cpp

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include "CPU.h" #include <iostream> CPU::CPU(const char *brand, const char *version) { this->brand = brand; this->version = version; std::cout << FUNCTION << std::endl; } CPU::~CPU() { std::cout << FUNCTION << std::endl; } |

Computer.h

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #pragma once #include "CPU.h" class Computer { public: Computer(const char *cpuBrand, const char *cpuVersion, int hardDisk, int memory); ~Computer(); private: CPU cpu; // Computer和CPU是"组合"关系 int hardDisk; //硬盘, 单位:G int memory; //内存, 单位:G }; |

Computer.cpp

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include "Computer.h" #include <iostream> Computer::Computer(const char *cpuBrand, const char *cpuVersion, int hardDisk, int memory):cpu(cpuBrand, cpuVersion) { this->hardDisk = hardDisk; this->memory = memory; std::cout << FUNCTION << std::endl; } Computer::~Computer() { std::cout << FUNCTION << std::endl; } |

Main.cpp

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <iostream> #include <Windows.h> #include <string> #include <string.h> #include "Computer.h" using namespace std; void test() { Computer a("intel", "i9", 1000, 8); } int main(void) { test(); system("pause"); return 0; } |

小结:被拥有的对象(芯片)的生命周期与其拥有者(计算机)的生命周期是一致的。计算机被创建时,芯片也随之创建。计算机被销毁时,芯片也随之销毁。拥有者需要对被拥有者负责,是一种比较强的关系,是整体与部分的关系。

具体组合方式:1)被组合的对象直接使用成员对象。(常用)

2)使用指针表示被组合的对象,在构造函数中,创建被组合的对象;在析构函数中,释放被组合的对象。

UML中的组合表示:

注意包含者使用实心菱形。【补充】UML画图工具:starUML

聚合

需求:给计算机配一台音响。

Computer.h

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #pragma once #include "CPU.h" class VoiceBox; class Computer { public: Computer(const char *cpuBrand, const char *cpuVersion, int hardDisk, int memory); ~Computer(); void addVoiceBox(VoiceBox *box); private: CPU cpu; // Computer和CPU是"组合"关系 int hardDisk; //硬盘, 单位:G int memory; //内存, 单位:G VoiceBox *box; //音箱 }; |

Computer.cpp

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include "Computer.h" #include <iostream> #include "VoiceBox.h" Computer::Computer(const char *cpuBrand, const char *cpuVersion, int hardDisk, int memory):cpu(cpuBrand, cpuVersion) { this->hardDisk = hardDisk; this->memory = memory; std::cout << FUNCTION << std::endl; } void Computer::addVoiceBox(VoiceBox *box) { this->box = box; } Computer::~Computer() { std::cout << FUNCTION << std::endl; } |

Main.cpp

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <iostream> #include <Windows.h> #include <string> #include <string.h> #include "Computer.h" #include "VoiceBox.h" using namespace std; void test(VoiceBox *box) { Computer a("intel", "i9", 1000, 8); a.addVoiceBox(box); } int main(void) { VoiceBox box; test(&box); system("pause"); return 0; } |

聚合不是组成关系,被包含的对象,也可能被其他对象包含。拥有者,不需要对被拥有的对象的生命周期负责。

UML中的组合表示:

相关推荐
云山工作室22 分钟前
基于单片机的温湿度采集系统(论文+源码)
人工智能·单片机·嵌入式硬件·毕业设计·毕设
天天进步201541 分钟前
Python项目--基于机器学习的股票预测分析系统
开发语言·python·机器学习
东方芷兰43 分钟前
Javase 基础入门 —— 02 基本数据类型
java·开发语言·笔记·spring·intellij-idea·idea
YuforiaCode1 小时前
第十五届蓝桥杯 2024 C/C++组 下一次相遇
c语言·c++·蓝桥杯
不是仙人的闲人1 小时前
算法之回溯法
开发语言·数据结构·c++·算法
chendilincd1 小时前
C++ 的史诗级进化:从C++98到C++20
java·c++·c++20
jacklood1 小时前
基于cubeMX的hal库STM32实现硬件IIC通信控制OLED屏
stm32·单片机·嵌入式硬件
程序小K1 小时前
利用Qt创建一个模拟问答系统
开发语言·qt
愚公搬代码1 小时前
【愚公系列】《Python网络爬虫从入门到精通》063-项目实战电商数据侦探(主窗体的数据展示)
开发语言·爬虫·python
碎梦归途2 小时前
23种设计模式-创建型模式之原型模式(Java版本)
java·开发语言·jvm·设计模式·原型模式