学习目标:学习此书u1【不包含后面编程练习和案例分析】
unit1 面向对象程序设计
使用软件:vs 2022
学习内容:
单元:unit1 面向对象程序设计OOP 使用软件:vs 2022
- 搭建 Java 开发环境
- 封装
- 继承
- 指针
- 多态
- c++面对对象程序设计
- 标准模板库
- 标准模板库的向量
- 数据结构与面向对象编程
学习时间:
2023/11/15 完成1,2,3节
预计:
2023/11/16 完成4,5,6节
2023/11/17 中断去补课“黑马程序员的函数模板部分”
2023/11/18 中断去补课”黑马程序员的STL部分“
2023/11/19 完成7,8,9节
1.抽象数据类型
提示:这里统计学习计划的总量
指定了操作的项被称为抽象数据类型ADT
然而,诸如C++之类的面向对象语言(OOL)与抽象数据类型有着直接的联系,
这种语言将OOL作为一个类来实现。
2.封装
对象是用类定义,类是一个模板,包括操作(函数或者方法)、数据成员
数据及其相关操作的结合称为对数据的封装
对象是类的实例,是用类定义创建的实体
封装的特点:
类内成员结合紧密(强耦合)
对象操作的有限原则(减少错误)
信息隐藏原则(对象)
提示:这里有示例代码
原代码:
class C {
public:
C( char*s="",int i, double d) {
//报错1:参数列表char*s=""
strcpy(dataMember1, s);
//报错2:strcpy不安全
dataMember2 = i;
dataMember3 = d;
}
void memberFunction1() {
cout<< dataMember1 << ' ' << dataMember2 << ' ' <<
dataMember3 << endl;
}
void memberFunction2(int i, char* s = "unkown") {
//报错5:同报错1
dataMember2 = i;
cout << i << "received from" << s << endl;
}
protected:
char dataMember1[10];
int dataMember2;
double dataMember3;
};
C object1("object1",100,200);
//报错3:"object1"类型与参数不符
C object2("object2");
C object3;
//报错4:类的声明需要括号
改法1
class C1 {
public:
C1(const char* s1, int i, double d) {
//原代码:
// 参数列表原本为char*s=""
// 是不可以这么做的,并且指向的是一个字符串
//【其实是字符型数组】不可更改,类型是const char *
// strcpy(dataMember1,s)
// 将s复制给数据成员1
// 但strcpy不安全且找不到有效解决方案
// 并且在创建对象时传入参数char*s=""为字符串"object1"
// 也不符合
// 字符串就是数组,在参数前面加const之后
// 可以通过下标赋值给数据成员1数组值
for (int i = 0; i < sizeof(s1); i++) {
dataMember1[i] = s1[i];
}
dataMember2 = i;
dataMember3 = d;
}
void memberFunction1() {
cout << dataMember1 << ' ' << dataMember2 << ' ' <<
dataMember3 << endl;
}
void memberFunction2(int i, const char* s ) {
// 改参数类型即可
dataMember2 = i;
cout << i << "received from" << s << endl;
}
protected:
char dataMember1[10];
int dataMember2;
double dataMember3;
};
C1 object1("object1",100,200);
C1 object2("object2");
C1 object3();
改法2
class C2 {
public:
C2( string s1, int i, double d) {
//原代码:
// 参数列表string s1处原本为char*s=""
// 新建对象时传入s1是一个字符串,则直接改为字符串
//string类内有一个函数是c_str就是返回
//当前字符串的首字符地址,其实也是一个数组
//就for循环直接下标赋值就好了
for (int i = 0; i < sizeof(s1); i++) {
dataMember1[i] = s1.c_str()[i];
}
dataMember2 = i;
dataMember3 = d;
}
void memberFunction1() {
cout<< dataMember1 << ' ' << dataMember2 << ' ' <<
dataMember3 << endl;
}
protected:
char dataMember1[10];
int dataMember2;
double dataMember3;
};
C++的强大特性之一是能够在类声明中使用类型参数来声明通用类。例如,如果需要声明一个使用数组存储数据的类,可以将该类声明为:
class intsto {
private:
int storage[50];
};
然而这种方式使得这个类仅适用于整数。如果一个类需要执行与intClass相同的操作,但操作的是浮点数或其他类型的数据等等,就需要声明新类。
更好的方法是声明一个通用类,只有在定义对象时才会确定对象引用什么类型的数据。幸运的是,C++允许以这种方式声明类,下面是这种类声明的示例:
提示:此处拓展一下模板的知识
template<class genType>
//声明类模板名 genType
class genClass {
public:
genType storage[50];
};
我们可以更进一步,可以不将 storage 数组的大小确定为50个单元,
而是将这个决定推迟到对象的定义阶段。
但是为了防止意外也可以使用一个默认值,所以可以将类声明为:
template<class genType,int size=50>
class genClass2 {
public:
genType storage[size];
};
这种使用通用类型的方法并不局限于类;在函数声明时同样适用。
eg。交换两个数值的函数
这里也是错的用于函数的模板不应该这样声明
template
template<typename genType>
void swap1(genType & i1, genType & i2) {
genType temp = i2;
i2 = i1;
i1 = temp;
}
这个示例还说明内置的运算符只能应用于特定的场合。
如果 genType是一个数值、一个字符或者是一个结构,
那么“ = ”号会正常地执行其功能。
但是如果genType是一个数组,swap就会遇到问题,为了解决这个问题,
可以重载赋值运算符, 在该运算符中加入特定数据类型所需要的功能。
在声明了通用函数之后,在编译期间,编译器会生成一个适当的函数。
例如:main函数中的调用
int a = 1;
int b = 2;
swap1<int>(a, b);
float c = 12.17;
float d = 3.14;
swap1(c, d);
3.继承
书上示例代码修改版及笔记
其中类内函数的参数原本都为char ,都改为const char
class BaseClass {
public:
BaseClass(){}
void pu(const char*s) {
cout << "pu() in baseClass called from " << s << endl;
}
protected:
void pro(const char* s) {
cout << "pro() in baseClass called from " << s << endl;
}
private:
void pri() {
cout << "pri() in baseClass called from " << endl;
}
};
class SonClass1 :public virtual BaseClass {
public:
void pu(const char* s) {
cout << "pu() in son1 called from " << s << endl;
pro("son1");
pri("son1");
}
void pri(const char*s) {
cout << "pri() in son1 called from " << s << endl;
}
};
class SonClass2 :public virtual BaseClass {
public:
void pu(const char* s) {
cout << "pu() in son2 called from " << s << endl;
pro("son1");
//pri("son1");
//报错因为父类private内容不可以访问
}
};
class Grandson : public SonClass1, public SonClass2 {
public:
void pu(const char* s) {
cout << "pu() in grandson called from " << s << endl;
pro("grandson");
SonClass1::pro("grandson");
BaseClass::pu("grandson");
}
};
//main函数代码
BaseClass papa;
SonClass1 son1;
SonClass2 son2;
Grandson grandson;
papa.pu("main(1)");
//父类protected和private类外都不可以访问
son1.pu("main(2)");
son1.pri("main(3)");
//son1在自己类内public重写的pu和pri类外都可以调用
//且pu函数内可调用继承的protected的pro函数,
//注意这里调用的pri是重写的,不是父类原本在private内的pri函数
son2.pu("main(4)");
//son2在自己类内public重写的pu类外可以调用
//且pu函数内可调用继承的protected的pro函数,
grandson.pu("main(5)");
//grandson在类外可以访问在public重写的pu函数,
//并在其中调用继承的在protected内的pro函数
//加上类的作用域还可以在类内访问到父类的public和protected的函数