当前位置: 首页 > news >正文

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语言的结构体用法)、

构造函数

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

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

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

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

构造函数的种类

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

默认构造函数

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

合成的默认构造函数

没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数。如果数据成员使用了“类内初始值”,就使用这个值来初始化数据成员【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;

}

......
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中的组合表示:

相关文章:

  • PyTorch入门------卷积神经网络
  • 医院数据中心智能化数据上报与调数机制设计
  • 2025mathorcup妈妈杯数学建模挑战赛C题:汽车风阻预测,详细思路,模型,代码更新中
  • 汽车免拆诊断案例 | 2019款大众途观L车鼓风机偶尔不工作
  • 零基础上手Python数据分析 (17):[案例实战] 电商销售数据分析 - 从数据到洞察的全流程演练
  • 磁流变式汽车减振器创新设计与关键技术研究
  • 【C++指南】哈希驱动的封装:如何让unordered_map/set飞得更快更稳?【上】
  • FreeRTOS菜鸟入门(七)·创建任务·静态任务创建
  • 网页端调用本地应用打开本地文件(PDF、Word、excel、PPT)
  • 再读bert(Bidirectional Encoder Representations from Transformers)
  • 动手学深度学习:手语视频在NiN模型中的测试
  • 万物互联时代,AWS IoT Core如何构建企业级物联网中枢平台?
  • MCP系列之实践篇:搭建你的第一个MCP应用
  • DemoGen:用于数据高效视觉运动策略学习的合成演示生成
  • Python 文本和字节序列(支持字符串和字节序列的双模式API)
  • Webview+Python:用HTML打造跨平台桌面应用的创新方案
  • DHTMLX宣布推出支持 Redux、TypeScript 和 MUI 的 React Gantt甘特图控件
  • xml+html 概述
  • 【前端HTML生成条形码——MQ】
  • 极狐GitLab 项目导入导出设置介绍?
  • 第152次中老缅泰湄公河联合巡逻执法行动圆满结束
  • 鞍钢矿业党委书记、董事长刘炳宇调任中铝集团副总经理
  • 往事|学者罗继祖王贵忱仅有的一次相见及往来函札
  • 长三角议事厅|国际产业转移对中国产业链韧性的影响与对策
  • 中国专家组赴缅开展地震灾害评估工作
  • 苗旋已任民航局空管局局长、党委副书记