目录
项目十 智能婚恋交友系统
第1节 项目需求
第2节 项目精讲-世界观的颠覆:面向对象的思想
第3节 项目精讲-女娲定义“人类”:类的使用
第4节 项目精讲-女娲造“人”:对象的基本使用
第5节 项目精讲-“生而不同”之构造函数
构造函数的作用
构造函数的特点
构造函数的种类
默认构造函数
第6节 合成的默认构造函数
第7节 手动定义的默认构造函数
第8节 自定义的重载构造函数
第9节 拷贝构造函数
手动定义的拷贝构造函数
合成的拷贝构造函数
第10节 什么时候调用拷贝构造函数
第11节 赋值构造函数
第12节 项目精讲-“最后的晚餐”之析构函数
第13节 项目精讲-永不迷失的真爱:this指针
第14节 项目精讲-类文件的分离
第15节 项目精讲-“大众情人”:静态数据成员
第16节 项目精讲-“不能拥有的方法”:静态成员函数
第17节 项目精讲-永葆初心之常成员
const数据成员
const成员函数
第18节 项目精讲-建模的常用手段:组合与聚合
组合
聚合
第19节 项目实现
第20节 常见错误总结
Error1-const
Error2-vector
Error2-const
Error3-static
第21节 英语不是障碍:计算机英语加油站
第22节 职场修炼:要不要加入创业团队?
第23节 逼格提升:不懂Linux的程序员 不是真正的程序员
第24节 项目练习
项目练习1
项目练习2
项目练习3
为看书困难的小伙伴推荐视频教程:百度网盘 提取码:r59a
婚恋交友-相亲
问题: 效率低下,时间成本大。
解决方案: 自动婚恋交友系统。 成功案例:百合网。
项目目标: 使用面向对象思想,开发自动相亲系统的核心框架。
面向过程: 什么是面向过程? 根据程序的执行过程,来设计软件的所有细节。
面向过程的缺点: 开发大型项目时,越来越难以把控,甚至失去控制。 后期维护、更新成本很大。
解决方案: 使用面向对象。
什么是面向对象? 不是面向对象,写代码:
面向对象是一种开发思想,一种全新的开发方式。
面向对象思想的重要性:
开发大型项目必备,是高级程序员的必备技能!
面向对象编程,最重要的第一个概念:类
“人类”是一个抽象的概念,不是具体的某个人。 “类”,是看不见,摸不着的,是一个纯粹的概念. “类”,是一种特殊的“数据类型”,不是一个具体的数据。
注意:类, 和基本数据类型(char/int/short/long/long long/float/double)不同
类的构成:方法和数据
类的设计
定义一个“人类”: Demo
#include
#include
#include
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 zhangshan;
system("pause");
}
什么是对象?
对象,是一个特定“类”的具体实例。
对象和普通变量有什么区别?
一般地,一个对象,就是一个特殊的变量,但是有跟丰富的功能和用法。
什么时候使用对象?
对象的具体使用方法
方式1
Demo1
int main(void)
{
Human h1; // 通过自定义的特殊数据类型“Human”类, 来创建一个“对象”
// 合法使用
h1.eat();
h1.play();
h1.sleep();
// 非法使用
// cout << "年龄" << h1.age << endl; //直接访问私有成员,将无法通过编译
//正确使用
cout << "年龄" << h1.getAge() << endl; //暴露问题,年龄值是一个很大的负数
system("pause");
}
总结:
“.”的使用
调用方法时,方法名后需要带一对圆括号()
通过对象,只能调用这个对象的public方法
分析:
多个不同的对象都有自己的数据,彼此无关。
方式2
Demo2
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】
否则,就使用默认初始化(实际上,不做任何初始化)
实例:demo3
#include
#include
#include
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;
}
注意:
只要手动定义了任何一个构造函数,编译器就不会生成“合成的默认构造函数”一般情况下,都应该定义自己的构造函数,不要使用“合成的默认构造函数”
【仅当数据成员全部使用了“类内初始值”,才宜使用“合成的默认构造函数”】
常称为“默认构造函数”
实例:demo4
#include
#include
#include
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
#include
#include
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;
}
Demo.
#include
#include
#include
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;
}
Demo.
#include
#include
#include
#include
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
#include
#include
#include
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;
}
调用函数时,实参是对象,形参不是引用类型 如果函数的形参是引用类型,就不会调用拷贝构造函数
函数的返回类型是类,而且不是引用类型
对象数组的初始化列表中,使用对象。
Demo
#include
#include
#include
#include
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;
}
Demo
#include
#include
#include
#include
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
使用方法:
不能主动调用。
对象销毁时,自动调用。
如果不定义,编译器会自动生成一个析构函数(什么也不做)
Demo
#include
#include
#include
#include
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;
}
demo1
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指针!
demo2
#include
#include
#include
#include
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;
}
demo3
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数据成员的初始化方式:
使用类内值(C++11支持)
使用构造函数的初始化列表
(如果同时使用这两种方式,以初始化列表中的值为最终初始化结果)
注意: 不能在构造函数或其他成员函数内,对const成员赋值!
Demo
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的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芯片也使用类来表示。
【参考代码】P10-组合
CPU.h
#pragma once
#include
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
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
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
#include
#include
#include
#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
需求: 给计算机配一台音响。
【参考代码】P10-聚合
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
#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
#include
#include
#include
#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中的组合表示:
回顾《项目需求》。
项目实现:
源码:P10-项目实现
Girl.h
#pragma once
#include
#include
using namespace std;
class Boy;
class Girl
{
public:
Girl();
Girl(int age, string name, int yanZhi);
~Girl();
int getAge()const;
string getName()const;
int getYanZhi()const;
bool satisfied(const Boy& s) const;
string description()const;
static void inputGirls(vector &girls);
private:
int age;
string name;
int yanZhi;
};
Girl.cpp
#include "Girl.h"
#include "Boy.h"
#include
#include
#define YANZHI_FACTOR 100
Girl::Girl() {
}
Girl::Girl(int age, string name, int yanZhi) {
this->age = age;
this->name = name;
this->yanZhi = yanZhi;
}
Girl::~Girl(){
}
int Girl::getAge() const {
return age;
}
string Girl::getName() const {
return name;
}
int Girl::getYanZhi() const {
return yanZhi;
}
bool Girl::satisfied(const Boy &s) const {
if (s.getSalary() >= yanZhi * YANZHI_FACTOR) {
return true;
}
else {
return false;
}
}
string Girl::description() const {
stringstream ret;
ret << name << "-女-颜值(" << yanZhi << ")-年龄(" << age << ")";
return ret.str();
}
void Girl::inputGirls(vector &girls) {
int age;
string name;
int yanZhi;
int n = 1;
while (1) {
cout << "请输入第" << n << "位小姐姐的年龄【输入0结束】:";
cin >> age;
if (age == 0) {
break;
}
cout << "请输入第" << n << "位小姐姐的姓名:";
cin >> name;
cout << "请输入第" << n << "位小姐姐的颜值:";
cin >> yanZhi;
n++;
girls.push_back(Girl(age, name, yanZhi));
}
}
Boy.h
#pragma once
#include
#include
using namespace std;
class Girl;
class Boy
{
public:
Boy();
Boy(int age, string name, int salary);
~Boy();
Boy(const Boy&other);
int getAge()const;
string getName()const;
int getSalary()const;
bool satisfied(const Girl& s) const;
string description() const;
static void inputBoys(vector &boys);
private:
int age;
string name;
int salary;
};
Boy.cpp
#include "Boy.h"
#include "Girl.h"
#include
#include
#define SALARY_FACTOR 0.006
Boy::Boy() {
}
Boy::Boy(const Boy&other) {
salary = other.salary;
name = other.name;
age = other.age;
}
Boy::Boy(int age, string name, int salary) {
this->age = age;
this->name = name;
this->salary = salary;
}
Boy::~Boy(){
}
int Boy::getAge() const {
return age;
}
string Boy::getName() const {
return name;
}
int Boy::getSalary() const {
return salary;
}
bool Boy::satisfied(const Girl &s) const {
if (s.getYanZhi() >= salary * SALARY_FACTOR) {
return true;
}
else {
return false;
}
}
string Boy::description()const{
stringstream ret;
ret << name << "-男-薪资(" << salary << ")-年龄(" << age << ")";
return ret.str();
}
void Boy::inputBoys(vector &boys) {
int age;
string name;
int salary;
int n = 1;
while (1) {
cout << "请输入第" << n << "位小哥哥的年龄【输入0结束】:";
cin >> age;
if (age == 0) {
break;
}
cout << "请输入第" << n << "位小哥哥的姓名:";
cin >> name;
cout << "请输入第" << n << "位小哥哥的薪资:";
cin >> salary;
n++;
boys.push_back(Boy(age, name, salary));
}
}
Main.cpp
#include
#include
#include
#include
#include
#include "Boy.h"
#include "Girl.h"
#include
void autoPair(const vector &boys,const vector &girls) {
for (int i = 0; i < boys.size(); i++) {
for (int j = 0; j < girls.size(); j++) {
if (boys[i].satisfied(girls[j]) &&
girls[j].satisfied(boys[i])) {
cout << boys[i].description() << "<==>" <<
girls[j].description() < boys;
vector girls;
Boy::inputBoys(boys);
Girl::inputGirls(girls);
cout << "\n\n------- 自动匹配 ------\n";
autoPair(boys, girls);
system("pause");
return 0;
}
/*
输入用例:
25 杨过 15000
28 郭靖 8000
35 段誉 50000
0
18 小龙女 95
25 如花 79
26 秋香 90
26 李莫愁 100
0
*/
const的错误用法
#include
#include
using namespace std;
class Man{
public:
Man(){}
void play() {
cout << "I am playing ...." << std::endl;
}
};
int main(void) {
const Man man;
man.play();
}
报错: error C2662: “void Man::play(void)”: 不能将“this”指针从“const Man”转换为“Man &”
原因: man是const对象, 但是却调用了非const方法. 类比: 专一男, 不能去夜店玩耍[因为这样很危险, 可能导致专一男变心]
解决方案:
方案1: 把 const Man man; 修改为: Man man;
方案2: 把play方法, 修改为 const方法.
vector加入的成员是拷贝新成员
Demo
#include
#include
#include
using namespace std;
class Man {
public:
Man() {}
void play() {
count += 10;
cout << "I am playing ...." << std::endl;
}
int getDrinkCount() const {
return count;
}
private:
int count = 0; //一共喝了多少杯酒
};
int main(void) {
Man zhangFei, guanYu, liuBei;
vector men;
// push_back是把参数的值,拷贝给vector
// men[0]的值和liubBei是相同的,但是,是两个不同的对象
men.push_back(liuBei);
men.push_back(guanYu);
men.push_back(zhangFei);
men[0].play();
cout << men[0].getDrinkCount() << endl; //10
cout << liuBei.getDrinkCount() << endl; //0
system("pause");
return 0;
}
#include
#include
using namespace std;
class Man{
public:
Man(){}
void play() const {
cout << "I am playing ...." << std::endl;
}
};
void play(Man &man) {
man.play();
}
int main(void) {
const Man man;
play(man);
}
原因: 非const引用, 不能对const变量进行引用
注意: const引用, 可以对非const变量进行引用
解决方案: 修改引用变量, 或者被引用的变量
#include
#include
using namespace std;
class Man{
public:
Man() { count++; }
void play() const {
cout << "I am playing ...." << std::endl;
}
int getAge() {
return age;
}
static int getCount() {
getAge(); //error!
return count;
}
private:
static int count;
int age;
};
int Man::count = 0;
int main(void) {
Man man1;
Man man2;
cout << Man::getCount() << endl;
system("pause");
return 0;
}
原因: 类的静态方法(static方法) 内, 不能访问实例方法(非static方法)和实例数据成员.
class |
|
---|---|
private |
|
public |
|
static |
|
const |
|
vector |
矢量, 向量 |
创业机会:程序员在技术沉淀后, 有很多创业机会.
创业方式:
自己作为核心, 自主创业.
以技术入股, 和其他伙伴一起创业
加入创业团队, 获取期权和广阔的发展空间.
创业中的坑:
判定自己是否合适创业 :创业是个无底洞. 如果只是为了获取自由, 做一个自由程序员, 性价比更高.
自己不要投入资金
判断项目是否靠谱
判断合伙人是否靠谱
程序员为什么要研究linux?
需要研究Linux的那些方面?
2.1)基本命令操作
2.2)Linux系统编程-服务器开发
3.应该选择Linux的哪些发行版本?
4.使用虚拟机方式还是硬盘安装?
创建一个类, 用来表示“玩具”
文具, 有以下数据:
名称,价格,产地。
在使用中,需要获取它的名称, 价格, 产地。
注意:根据自己当前的优惠情况,有一个对外的价格。
参考代码:
Toy.h
#pragma once
#include
using namespace std;
class Toy
{
public:
Toy();
Toy(string name, int price, string origin);
~Toy();
string getName() const;
int getPrice() const;
string getOrigin() const;
void setDiscount(float discount);
private:
string name;
int price;
string origin; //产地
float discount = 1.0; //则扣
};
Toy.cpp
#include "Toy.h"
Toy::Toy()
{
}
Toy::Toy(string name, int price, string origin)
{
this->name = name;
this->price = price;
this->origin = origin;
}
Toy::~Toy()
{
}
string Toy::getName() const
{
return name;
}
int Toy::getPrice() const
{
return price * discount;
}
string Toy::getOrigin() const
{
return origin;
}
void Toy::setDiscount(float discount)
{
this->discount = discount;
}
Main.cpp
#include
#include "Toy.h"
int main(void) {
Toy toy("变形金刚", 5600, "China");
cout << toy.getName() << ": " << toy.getPrice()
<< "[mode in " << toy.getOrigin() << "]"<
定义一个或多个类,来描述以下需求:
定义一个类,来表示某模拟养成游戏中人物:
每个人物, 有昵称,年龄,性别, 配偶, 朋友,
支持的活动有:结婚,离婚, 交友,断交,询问昵称,询问性别,询问年龄, 简介等。
参考代码:
Human.h
#pragma once
#include
#include
using namespace std;
typedef enum gender
{
MAN, //男
WOMAN //女
}gender_t;
class Human
{
public:
Human();
Human(const string &name, gender_t gender, int age);
~Human();
string getName() const;
gender getGender() const;
int getAge() const;
Human* getLover() const;
vector getFriends() const;
string description() const;
void marry(Human &other);
void divorce();
void addFriend(Human &other );
void delFriend(Human &other);
private:
string name;
gender_t gender; //性别
int age;
Human *lover; //配偶,爱人
vector friends;
};
Human.cpp
#include "Human.h"
#include
Human::Human()
{
}
Human::Human(const string & name, gender_t gender, int age)
{
this->name = name;
this->gender = gender;
this->age = age;
}
Human::~Human()
{
}
string Human::getName() const
{
return name;
}
gender Human::getGender() const
{
return gender;
}
int Human::getAge() const
{
return age;
}
Human * Human::getLover() const
{
return lover;
}
vector Human::getFriends() const
{
return friends;
}
string Human::description() const
{
stringstream des;
des << name << "-age:" << age << "-" << (gender == MAN ? "男" : "女");
return des.str();
}
void Human::marry(Human &other)
{
if (gender == other.gender) {
return;
}
this->lover = &other;
other.lover = this;
}
void Human::divorce()
{
if (this->lover == NULL) {
return;
}
lover->lover = NULL;
lover = NULL;
}
void Human::addFriend(Human & other)
{
friends.push_back(&other);
}
void Human::delFriend(Human & other)
{
for (auto it = friends.begin(); it != friends.end(); ) {
if (*it == &other) {
it = friends.erase(it); //返回下一个成员的"迭代器"
}
else {
it++;
}
}
}
Main.cpp
#include
#include "Human.h"
int main(void) {
Human lhc("令狐冲", MAN, 25);
Human ryy("任盈盈", WOMAN, 26);
Human tbg("田伯光", MAN, 30);
Human yls("岳灵珊", WOMAN, 20);
Human cx("冲虚道长", MAN, 55);
lhc.marry(yls);
Human *who = lhc.getLover();
cout << lhc.getName() << "的配偶是: " << who->description() << endl;
cout << who->getName() << "的配偶是: " << who->getLover()->description() << endl;
cout << lhc.getName() << "离婚." << endl;
lhc.divorce();
if (lhc.getLover() == NULL) {
cout << lhc.getName() << "单身" << endl;
}
lhc.addFriend(cx);
lhc.addFriend(tbg);
vector friends = lhc.getFriends();
cout << lhc.getName() << "的朋友:" << endl;
for (int i = 0; i < friends.size(); i++) {
cout << friends[i]->description() << endl;
}
cout << lhc.getName() << "删除好友:" << tbg.getName() << endl;
lhc.delFriend(tbg);
friends = lhc.getFriends();
cout << lhc.getName() << "的朋友:" << endl;
for (int i = 0; i < friends.size(); i++) {
cout << friends[i]->description() << endl;
}
system("pause");
return 0;
}
1.定义一个或多个类,来描述以下需求:
汽车,有多个轮胎,一个发动机,品牌,型号, 价格, 行驶里程。
轮胎,有品牌,尺寸,气压。
发动机,有品牌,型号。
参考代码: Tire.h
#pragma once
#include
using namespace std;
class Tire
{
public:
Tire(const string &brand="米其林");
~Tire();
string descripton() const;
string getBrand() const;
float getPressure() const;
private:
string brand; //品牌
float pressure; //胎压
};
Tire.cpp
#include "Tire.h"
#include
Tire::Tire(const string &brand)
{
this->brand = brand;
this->pressure = 2.5;
}
Tire::~Tire()
{
}
string Tire::descripton() const
{
stringstream ret;
ret << "品牌:" << brand << "-胎压:" << pressure;
return ret.str();
}
string Tire::getBrand() const
{
return brand;
}
float Tire::getPressure() const
{
return pressure;
}
Engine.h
#pragma once
#include
using namespace std;
class Engine
{
public:
Engine();
Engine(const string &brand, float version);
~Engine();
string description() const;
private:
string brand; //品牌
float version; //型号, x.x升
};
Engine.cpp
#include
#include "Engine.h"
Engine::Engine()
{
}
Engine::Engine(const string & brand, float version)
{
this->brand = brand;
this->version = version;
}
Engine::~Engine()
{
}
string Engine::description() const
{
stringstream ret;
ret << "品牌:" << brand << "-型号:" << version;
return ret.str();
return string();
}
Car.h
#pragma once
#include
#include "Engine.h"
#include "Tire.h"
using namespace std;
class Car
{
public:
Car();
Car(const string &carBrand, const string &carVer, int carPrice,
const string &engineBrand, float engineVer,
const string &tireBrand = "米其林");
~Car();
Engine getEngine();
Tire* getTire(int i);
string getBrand();
string getVersion();
int getPrice();
int getMiles();
string description();
private:
Engine engine;
Tire tires[4];
string brand; //品牌
string version; //型号
int price;
int miles;
};
Car.cpp
#include
#include "Car.h"
Car::Car()
{
}
Car::Car(const string & carBrand, const string & carVer, int carPrice,
const string & engineBrand, float engineVer,
const string & tireBrand):
engine(engineBrand, engineVer), tires{ tireBrand, tireBrand, tireBrand, tireBrand }
{
this->brand = carBrand;
this->price = carPrice;
this->version = carVer;
this->miles = 0;
}
Car::~Car()
{
}
Engine Car::getEngine()
{
return engine;
}
Tire* Car::getTire(int i)
{
if (i>=1 && i<=4) {
return &tires[i];
}
return NULL;
}
string Car::getBrand()
{
return brand;
}
string Car::getVersion()
{
return version;
}
int Car::getPrice()
{
return price;
}
int Car::getMiles()
{
return miles;
}
string Car::description()
{
stringstream ret;
ret << "汽车品牌:" << brand << "-" << version << "-$" << price
<< "\t\t引擎:" << engine.description()
<< "\t\t轮胎:" << tires[0].descripton();
return ret.str();
}
测试代码:
#include
#include
#include "Car.h"
int main(void) {
{
Car car("宝马", "X7", 950000, "宝马", 3.5);
cout << car.description() << endl;
}
system("pause");
return 0;
}
【说明】友元,调整到类的继承之后再讲解
为看书困难的小伙伴推荐视频教程:百度网盘 提取码:r59a