面向对象的思想
面向过程:
根据程序的执行过程,来设计软件的所有细节。面向过程的缺点:开发大型项目时,越来越难以把控,甚至失去控制。后期维护、更新成本很大。解决方案:使用面向对象。
什么是面向对象?
不是面向对象,写代码:面向对象是一种开发思想,一种全新的开发方式。面向对象思想的重要性:开发大型项目必备,是高级程序员的必备技能!
类的使用
面向对象编程,最重要的第一个概念:类。"人类"是一个抽象的概念,不是具体的某个人。"类",是看不见,摸不着的,是一个纯粹的概念。"类",是一种特殊的"数据类型",不是一个具体的数据。注意:类, 和基本数据类型(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语言的结构体用法)、
构造函数
构造函数的作用:在创建一个新的对象时,自动调用的函数,用来进行"初始化"工作:对这个对象内部的数据成员进行初始化。
构造函数的特点:自动调用(在创建新对象时,自动调用)、构造函数的函数名,和类名相同、
-
构造函数没有返回类型
-
可以有多个构造函数(即函数重载形式)
构造函数的种类
默认构造函数、自定义的构造函数、拷贝构造函数、赋值构造函数
默认构造函数
没有参数的构造函数,称为默认构造函数。
合成的默认构造函数
没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数。如果数据成员使用了"类内初始值",就使用这个值来初始化数据成员【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中的组合表示:
