C++面向对象
面向对象的思想
面向过程:
根据程序的执行过程,来设计软件的所有细节。面向过程的缺点:开发大型项目时,越来越难以把控,甚至失去控制。后期维护、更新成本很大。解决方案:使用面向对象。
什么是面向对象?
不是面向对象,写代码:面向对象是一种开发思想,一种全新的开发方式。面向对象思想的重要性:开发大型项目必备,是高级程序员的必备技能!
类的使用
面向对象编程,最重要的第一个概念:类。“人类”是一个抽象的概念,不是具体的某个人。“类”,是看不见,摸不着的,是一个纯粹的概念。“类”,是一种特殊的“数据类型”,不是一个具体的数据。注意:类, 和基本数据类型(char/int/short/long/long long/float/double)不同。
类的构成:方法和数据
类的设计
对象的基本使用
对象,是一个特定“类”的具体实例。对象和普通变量的区别:一般地,一个对象,就是一个特殊的变量,但是有跟丰富的功能和用法。
方式一
#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
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语言的结构体用法)、
构造函数
构造函数的作用:在创建一个新的对象时,自动调用的函数,用来进行“初始化”工作:对这个对象内部的数据成员进行初始化。
构造函数的特点:自动调用(在创建新对象时,自动调用)、构造函数的函数名,和类名相同、
-
构造函数没有返回类型
-
可以有多个构造函数(即函数重载形式)
构造函数的种类
默认构造函数、自定义的构造函数、拷贝构造函数、赋值构造函数
默认构造函数
没有参数的构造函数,称为默认构造函数。
合成的默认构造函数
没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数。如果数据成员使用了“类内初始值”,就使用这个值来初始化数据成员【C++11】。否则,就使用默认初始化(实际上,不做任何初始化)。
#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;
}
注意:只要手动定义了任何一个构造函数,编译器就不会生成“合成的默认构造函数”。一般情况下,都应该定义自己的构造函数,不要使用“合成的默认构造函数”【仅当数据成员全部使用了“类内初始值”,才宜使用“合成的默认构造函数”】。
手动定义的默认构造函数
常称为“默认构造函数”
#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;
}
说明:如果某数据成员使用类内初始值,同时又在构造函数中进行了初始化,那么以构造函数中的初始化为准。相当于构造函数中的初始化,会覆盖对应的类内初始值。
自定义的重载构造函数
#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;
}
拷贝构造函数
手动定义的拷贝构造函数
#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;
}
合成的拷贝构造函数
#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;
}
说明:合成的拷贝构造函数的缺点: 使用“浅拷贝”。解决方案:在自定义的拷贝构造函数中,使用‘深拷贝
#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;
}
什么时候调用拷贝构造函数
调用函数时,实参是对象,形参不是引用类型。如果函数的形参是引用类型,就不会调用拷贝构造函数。函数的返回类型是类,而且不是引用类型,对象数组的初始化列表中,使用对象。
#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;
}
赋值构造函数
#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; } |
...... 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中的组合表示: