类的一些特性
知识点1:可通过typedef或using类可以自定义某种类型别名在类中
知识点2:如果我们需要合成的默认构造函数,在空列表的构造函数后加上=default即可
知识点3:类的成员也可以重载,参数列表数量或者类型上不同
知识点4:在变量前加关键字mutable,变为可变数据成员,即使是在一个const的对象函数中也可以被修改
知识点5:类内初始值,直接用=赋予的方式是C++11新特性,也就是VS2013以上的版本才支持
知识点6:当某个数据成员在构造函数初始化列表中忽略,它将以默认构造函数的方式隐式初始化,但需要有一个初始化值,否则应该显式的初始化
为什么使用size_type类型
机器无关的(machine-independent)。
与unsigned型(unsigned int或unsigned long)具有相同的含义,而且可以保证足够大可存储任意string对象的长度。
为了使用由string类型定义的size_type类型,程序员必须加上作用域操作符来说明所使用的size_type类型是由string类定义的。
不要把size的返回值赋给一个int变量
unsigned型所能表示的最大正数值比对应的signed要大一倍。
这个事实表明size_type存储的string长度是int所能存储的两倍。
使用int变量的另一个问题是,有些机器上int变量的表示范围太小,甚至无法存储实际并不长的string对象。如在有16位int型的机器上,int类型变量最大只能表示32767个字符的string对象。
而能容纳一个文件内容的string对象轻易就会超过这个数字。因此,为了避免溢出,保存一个string对象size的最安全的方法就是使用标准库类型string:: size_type。
string类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能和机器无关(machine-independent)。string::size_type定义为unsigned型,可以保证足够大的存储string对象的长度。
注意,任何存储string的size操作结果的变量必须为string::size_type类型。尤其不能把size的返回值赋给一个int变量。(因为size返回的是一个unsigned类型,而int是signed类型。size能表达的大小是int的2倍)。
为什么使用inline函数
inline内联函数目的是为了解决程序中函数调用的效率问题。一般函数进行调用时,要将程序的执行权转到被调用的函数中,然后再返回到调用它的函数中;而内联函数在进行调用时,是将调用表达式用内联函数体来替换。
类中函数默认是inline函数
在类中对于公共代码使用私有功能函数,可以避免在多处使用同样的代码减少代码量,在调试时只需要修改定义的函数,在类内的函数为内联函数并不会产生额外的开销。
类的练习-字符方阵
using namespace std;
class Screen {
public:
using pos = string::size_type;
Screen() = default;
Screen(pos wd, pos ht, char c) :height(ht), width(wd), contents(ht*wd, c) {}
Screen(pos wd, pos ht) :height(ht), width(wd), contents(ht*wd, NULL) {}
pos &getcursor() { return cursor; }
char get() const { return contents[cursor]; }
char get(pos r, pos c) { return contents[r*width + c]; }
Screen &move(pos r, pos c) { cursor = ((r-1)*width + (c-1)); return *this; }
Screen &set(char letter) { contents[cursor] = letter; return *this; }
Screen &display(ostream &out) { do_display(out); return *this; }
const Screen &display(ostream &out) const { do_display(out); return *this; }
private:
pos cursor{ 0 };
pos height{ 0 }, width{ 0 };
string contents;
void do_display(ostream &out) const {
for (pos index = 0; index != height * width; ++index) {
if (index%width == 0)out << endl;
out << contents[index] << " ";
}
}
};
int main(int *argc, char *argv) {
string::size_type row_num, column_num, resetrow,resetcolumn;
char start_letter, resetletter;
cout << "输入字符方阵的行数: ";cin >> row_num;
cout << "输入字符方阵的列数: "; cin >> column_num;
cout << "输入每个单位的初始化字符: "; cin >> start_letter;
cout << "输入要改变字符单位的所在行数: "; cin >> resetrow;
cout << "输入要改变字符单位的所在列数: "; cin >> resetcolumn;
cout << "输入要改变成的字符: "; cin >> resetletter;
Screen myScreen(row_num,column_num,start_letter);
myScreen.move(resetrow, resetcolumn).set(resetletter).display(cout);
cout << endl;
myScreen.display(cout);
cout << endl;
return 0;
}
this指针的好与坏
优点:1:当需要将一个对象作为整体引用而不是引用对象的一个成员时,使用this,则该函数返回对调用该函数的对象的引用。
return *this;
2:可以非常明确地指出访问的是调用该函数的对象的成员,且可以在成员函数中使用与数据成员同名的形参。
class one{
public:
void func(int member){
this->member = member;
}
private:
int member;
};
缺点:不必要使用,代码多余。
class two{
public:
void func(){
cout<<member;//cout<<this->member;
}
private:
int member;
};
友元
如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。
友元类
若在A类中有friend class B;
B类可以访问A类所有权限的成员
友元成员函数
若在A类中有friend void B::ItsFunction(Function's Parameter);
B中的ItsFunction函数可以访问A类的所有权限成员
声明友元函数对类的定义顺序的要求
①首先定义Window_mgr类,其中声明clear函数,但是不能定义它。在clear使用Screen的成员之前必须先声明Screen
②接下来定义Screen,包括对于clear的友元声明。
③最后定义clear,此时它才可以使用Screen的成员。
using namespace std;//这段代码用来练习如何声明友元函数
class Window_mgr {//但经过编译执行并不能正确地通过Window_mgr对象将某个screen对象清空(初始化字符),是由于类的定义顺序问题
public:
using ScreenIndex = vector<Screen>::size_type;
void clear(size_t);
private:
vector<Screen> screens{ Screen(24,80,' ') };
};
class Screen {
//friend class Window_mgr;
friend void Window_mgr::clear(size_t);
public:
using pos = string::size_type;
Screen() = default;
Screen(pos wd, pos ht, char c) :height(ht), width(wd), contents(ht*wd, c) {}
Screen(pos wd, pos ht) :height(ht), width(wd), contents(ht*wd, NULL) {}
pos &getcursor() { return cursor; }
char get() const { return contents[cursor]; }
char get(pos r, pos c) { return contents[r*width + c]; }
Screen &move(pos r, pos c) { cursor = ((r-1)*width + (c-1)); return *this; }
Screen &set(char letter) { contents[cursor] = letter; return *this; }
Screen &display(ostream &out) { do_display(out); return *this; }
const Screen &display(ostream &out) const { do_display(out); return *this; }
private:
pos cursor{ 0 };
pos height{ 0 }, width{ 0 };
string contents;
void do_display(ostream &out) const {
for (pos index = 0; index != height * width; ++index) {
if (index%width == 0)out << endl;
out << contents[index] << " ";
}
}
};
void Window_mgr::clear(size_t i) {
Screen &s = screens[i];
s.contents = string(s.width*s.height, ' ');
}
int main(int *argc, char *argv) {
return 0;
}
下面这段代码可以正确地通过Window_mgr对象将6x6字符都是n的screen对象归零/初始化(都变成星),因为没有用友元函数用的是友元类
using namespace std;
class Screen {
friend class Window_mgr;
//friend void Window_mgr::clear(size_t);
public:
using pos = string::size_type;
Screen() = default;
Screen(pos wd, pos ht, char c) :height(ht), width(wd), contents(ht*wd, c) {}
Screen(pos wd, pos ht) :height(ht), width(wd), contents(ht*wd, NULL) {}
pos &getcursor() { return cursor; }
char get() const { return contents[cursor]; }
char get(pos r, pos c) { return contents[r*width + c]; }
Screen &move(pos r, pos c) { cursor = ((r-1)*width + (c-1)); return *this; }
Screen &set(char letter) { contents[cursor] = letter; return *this; }
Screen &display(ostream &out) { do_display(out); return *this; }
const Screen &display(ostream &out) const { do_display(out); return *this; }
private:
pos cursor{ 0 };
pos height{ 0 }, width{ 0 };
string contents;
void do_display(ostream &out) const {
for (pos index = 0; index != height * width; ++index) {
if (index%width == 0)out << endl;
out << contents[index] << " ";
}
}
};
class Window_mgr {
public:
using ScreenIndex = vector<Screen>::size_type;
void clear(size_t);
Screen &getScr(ScreenIndex i) { return screens[i]; }
void addScr(Screen scr) { screens.push_back(scr); }
private:
vector<Screen> screens{};
};
void Window_mgr::clear(size_t i) {
Screen &s = screens[i];
s.contents = string(s.width*s.height, '*');
}
int main(int *argc, char *argv) {
Window_mgr Manager;
Screen scr0(6,6,'n');
Manager.addScr(scr0);
Manager.getScr(0).display(cout);
cout << endl;
Manager.clear(0);
Manager.getScr(0).display(cout);
cout << endl;
return 0;
}
其他注意事项
友元关系不具有传递性,每个类负责控制自己的友元类或友元函数。
对于要将一组重载函数设定为友元,对于每一个不同参数的版本都要分别声明。
在类内声明的友元必须在类内或者类外进行定义之后才可以使用。
关于Employee的类
#include"stdafx.h"
#include<iostream>
#include<string>
#include<vector>
#include<windows.h>
using namespace std;
class Employee {
private:
const string Name;
unsigned Age;
const float BodyHeight;
float Weight;
const string MainSkill;
string SecondSkill;
unsigned salary;
string character;
float WorkEffect;
public:
Employee(string name, unsigned age, float height, float weight,string main) :Name(name), Age(age), BodyHeight(height), Weight(weight),MainSkill(main) {
cout << name << "的次级能力: ";cin >> SecondSkill;
cout << name << "的目前薪水: "; cin >> salary;
cout << name << "的性格描述: "; cin >> character;
cout << name << "的工作贡献评分: "; cin >> WorkEffect;
}
void DisplayInfo() {
cout << "\n姓名: " << Name;
cout << "\n年龄: " << Age;
cout << "\n身高: " << BodyHeight;
cout << "\n体重: " << Weight;
cout << "\n主要技能: " << MainSkill;
cout << "\n次要技能: " << SecondSkill;
cout << "\n目前薪水: " << salary;
cout << "\n性格描述: " << character;
cout << "\n工作贡献评分: " << WorkEffect<<endl;
}
};
int main(int* argc, char *argv) {
unsigned EmployeeCount;
cout << "共有几位员工等待输入? "; cin >> EmployeeCount;
vector<Employee> EmployeesVec{};
string tempname,tempmain; unsigned tempage; float tempheight, tempweight;
for (unsigned i = 1; i <= EmployeeCount;i++) {
system("cls");
cout << "输入第" << i << "位员工的姓名: "; cin >> tempname;
cout << tempname << "的年龄: "; cin >> tempage;
cout << tempname << "的身高: "; cin >> tempheight;
cout << tempname << "的体重: "; cin >> tempweight;
cout << tempname << "的主要技能: "; cin >> tempmain;
Employee tempEmployee(tempname,tempage,tempheight,tempweight,tempmain);
EmployeesVec.push_back(tempEmployee);
}
cout << EmployeeCount << "位员工的信息录入完成。";
char choice;
cout << "是否输出所有员工信息?Y/N";
if (cin >> choice) {
switch (choice)
{
case 'y':
case 'Y': {
for (unsigned i = 0; i != EmployeeCount; i++) {
EmployeesVec[i].DisplayInfo();
}
break;
}
default:
break;
}
}
return 0;
}