类和对象:
类:数据类型。c++有内建类(如 string),程序员可以扩展自己的类(如 DirectorCut);通过类声明来创建一个类。
类声明:描述了其中的数据成员和成员函数。(类声明的花括号后有分号 ; )
//类声明
class Film{
/*******/
};
//末尾有分号
对象的定义:在C++中以一个类作为数据类型定义的变量就是对象。如:
class Film{
//定义一个Film对象
Film f;
//定义对象数组
Film films[30];
//或者是 class Film f; 也可
};
C++对C的结构体进行拓展;关键字struct可以创建一个类;
class创建的类中成员默认为private,struct创建的类中成员默认为public.
C++的信息隐藏机制
private/public/protected
面向对象设计:
用private隐藏类实现,public暴露类接口。
一个简单的类定义:
#include<iostream>
#include<string>
using namespace std;
class Film {
public:
void setName();
void setDate();
private:
string Name;
string Date;
};
(其中public与private可以交替出现)
上述Film类的客户可以通过调用公有成员函数 getName 和 getDate 请求Film类提供服务;但是客户不能访问属于类实现部分的私有成员 Name 和 Date .
成员选择符和指针操作符:
可对类的数据成员&成员函数进行访问。
Film f;
f.setName();
类范围:
类的私有成员仅能由类的成员函数访问;类的公有成员有公有范围性质,可以在类外访问。对象的使用者只能访问类的公有成员(数据成员或成员函数)。
类成员函数:
类内声明,类外定义。
若在类声明之中定义则称为内联函数(inline);在声明时使用inline关键字,可将原本定义在类声明之外的成员函数强制编成内联函数。
#include<iostream>
using namespace std;
class Person {
public:
inline void setAge(unsigned n);
private:
unsigned age;
};
void Person::setAge(unsigned n) {
age = n;
}
域解析符 ' : ' 避免重名
在程序中使用类:
类声明、对象定义、客户服务请求
程序的效率和健壮性:
通过引用来传递和返回对象
对象的传递和返回方式:传值或引用。尽量采用引用的方式进行对象的传递和返回;因为传值的方式效率低,需要进行对象间的拷贝,数据增大浪费内存。传递一个指向对象的指针可以起到效率相同的效果,但是引用方式的语法简单。
关于const:
对象通过引用方式传到函数f中,且f不通过修改对象的数据成员来改变对象的状态,可以将f的参数标记为const来防止对参数的误写。
//将函数setName的string类型的参数标记为const
//表明setName不会改变n值
//仅将n值赋给成员name
#include<iostream>
using namespace std;
class C {
public:
void setName(const string& n) {
name = n;
};
private:
string name;
};
一个成员函数不需要直接或者间接(值通过调用其他成员函数)地改变该函数所属对象的任何数据成员,最好将这个函数标记为const;可以防止对该函数所属对象的数据成员的误写。
#include<iostream>
using namespace std;
class C {
public:
void setName(string& n) {
name = n;
};
string get() const { return name; }
//注意const所处的位置:参数列表与函数体之间
private:
string name;
};
由此可知一个const成员函数仅能调用其他const成员函数,因为const成员函数不能直接或者间接地改变对象的状态;若调用非const型成员函数可能会间接改变对象的状态。
函数若采用const返回,返回值必赋给const类型的局部变量;如果该const返回值是一个类的指针或引用,则不能用该指针或引用调用该类的nonconst成员函数,因为可能改变数据成员的值。
例:
//对成员函数进行重载以处理两种类型的字符串
class C{
public:
void set (const string& n){
name = n;
}
void set (const char* n){
name = n;
}
const string& get() const {
return name;
}
private:
string name;
}
作业:堆栈类
class Stack
#include<iostream>
using namespace std;
class Stack {//此栈是用了一个数组
public:
enum { MaxStack = 5 }; //关键字enum,枚举类型;(栈顶元素为5,之后依次为678...//?)
void init() { top = -1; }//初始化top,push和top操作的正确性,stack创建时应该首先调用的成员函数,如果是一个非空stack则会清空该stack
void push(int n)
{
if (isFull())//判断堆栈是否已满
{
errMsg("Stack is full; Can't push.");//error message,自定义错误信息
return;
}
arr[++top] = n;//之前已经初始化top = -1;++top = 0.存入整型n,位置是arr[0]
}
int pop() //弹出top,top--
{
if (isEmpty()) //是否为空
{
errMsg("Stack is empty. Popping dummy value");
return dummy_val;//返回任意值//dummy_val?
}
return arr[top--];//返回下一个,循环检测是否为空
}
bool isEmpty() { return top < 0; }
bool isFull() { return top >= MaxStack - 1; }
void dump()//降序输出元素
{
cout << "Stack eles,top to bottom:\n";
for (int i = top; i >= 0; i--)
cout << '\t' << arr[i] << '\n';
}
//以下部分不很理解
//------------------
private://只有类本身才能调用下面的成员函数
void errMsg(const char* msg)const {
//打印错误信息,关联到标准错误,通常写入到与标准输出相同的设备。默认情况下,写到cerr的数据是不缓冲的。
//Cerr通常用于输出错误信息与其他不属于正常逻辑的输出内容。
cerr << "\n*** Stack opration failure:" << msg << endl;
}//也就是有了错误信息时开始匹配到这个函数
//-------------------
int top;
int arr[MaxStack];
int dummy_val;
};
main函数
#include "Stack.cpp"
int main()
{
Stack s1;
s1.init();
s1.push(5);
s1.push(6);
s1.dump();
cout << "Poping: " << s1.pop() << endl;
s1.dump();
s1.push(7);
s1.dump();
cout << "Poping1: " << s1.pop() << " Poping2: " << s1.pop() << endl;
s1.dump();//已经清空
s1.pop();//已经为空栈时,调用清空pop则会调用错误输出cerr
s1.push(5);
s1.push(6);
for (unsigned i = 0; i < Stack::MaxStack; i++)//i<4,就压入8,后面就是满了调用了isFull函数,自定义错误信息
{
s1.push(7);
}
s1.dump();
return 0;
}
输出结果:
————————2021-11-03-18:32————————