Part 1 车辆基本信息管理
1、代码
1 #include<iostream> 2 using namespace std; 3 4 #include"car.h" 5 #include "ElectricCar.h" 6 #include "Battery.h" 7 int main() 8 { 9 Car oldcar("Audi", "a4", 2016); 10 cout << "--------oldcar's info--------" << endl; 11 oldcar.updateodometer(2500); 12 cout << oldcar << endl; 13 oldcar.updateodometer(2400);//测试出错情况 14 cout << oldcar << endl; 15 16 ElectricCar newcar("Tesla", "model s", 2016); 17 newcar.updateodometer(2500); 18 cout << "\n--------newcar's info--------\n"; 19 cout << newcar << endl; 20 system("pause"); 21 return 0; 22 }
1 #pragma once 2 #include "Car.h" 3 #include"Battery.h" 4 #include<iostream> 5 using namespace std; 6 class Battery;//友元类的声明 7 class ElectricCar:public Car 8 { 9 public: 10 ElectricCar(string ker = "producer", string mod = "A1", int y = 1984, double s = 0); 11 friend ostream & operator<<(ostream &out, ElectricCar &e); 12 ~ElectricCar(); 13 14 private: 15 Battery b; 16 int a;//b.battery的值的副本存储 17 };
1 #include "ElectricCar.h" 2 using namespace std; 3 4 ElectricCar::ElectricCar(string ker, string mod, int y, double s):Car(ker,mod,y,s)//构造函数 5 { 6 b.batterysize = 70;//初始化Battery中的私有成员的值 7 a = b.batterysize; 8 } 9 10 ostream & operator<<(ostream &out,ElectricCar &e) 11 { 12 out << "maker:" << e.getker() << "\n" << "model:" << e.getmod() << "\n" << "year:" << e.gety() << "\n" << "odometer:" << e.gets()<<"\n"<<"batterysize:"<< e.a<<"-kWh"<< endl; 13 return out; 14 } 15 16 ElectricCar::~ElectricCar() 17 { 18 }
1 #pragma once 2 class Battery 3 { 4 public: 5 Battery(int basize=70); 6 ~Battery(); 7 friend class ElectricCar;//声明为友元类 8 private: 9 int batterysize; 10 };
1 #include "Battery.h" 2 3 Battery::Battery(int basize):batterysize(basize) 4 { 5 } 6 7 Battery::~Battery() 8 { 9 }
1 #pragma once 2 #include<string> 3 #include<iostream> 4 using std::string; 5 using std::cout; 6 using std::cin; 7 using std::ostream; 8 9 class Car 10 { 11 public: 12 Car(string ker="producer",string mod="A1",int y=1984,double s=0 ); 13 ~Car(); 14 friend ostream & operator<<(ostream &out,Car &c); 15 void updateodometer(double s); 16 string getker() { return maker; } 17 string getmod() { return model; } 18 int gety() { return year; } 19 double gets() { return odo; } 20 private: 21 string maker; 22 string model; 23 int year; 24 double odo; 25 };
1 #include "Car.h" 2 using namespace std; 3 4 //构造函数 5 Car::Car(string ker,string mod,int y,double s ):maker(ker),model(mod),year(y),odo(s) 6 { 7 } 8 9 void Car::updateodometer(double s) 10 { 11 double ss = odo; 12 odo = s; 13 if (s < ss) 14 { 15 cout << "Warn! Update is wrong.Please input again:"; 16 cin >> s; 17 updateodometer(s); 18 } 19 } 20 21 //<<运算符重载 22 ostream & operator<<(ostream &out,Car &c) 23 { 24 out <<"maker:" << c.maker << "\n"<< "model:" << c.model << "\n"<< "year:" << c.year << "\n"<< "odometer:" << c.odo << endl; 25 return out; 26 }
2、程序结果截图
3、相关说明
(1)题目是Battery类作为派生类ElectricCar的新增成员数据要求返回Battery类中私有成员的值,不知道咋实现。所以我
①将类ElectriCar声明为了Battery的友元类,以此在E类中访问B类中的私有成员。详情见Battery.h中的第7行和ElectricCar.h中的第6(友元类声明),15行。(应该也可以用组合类的方式)
②E类 中还有另一个私有数据成员int a,是b.battery的值的副本,为了直接在operator<<中直接使用e.a调用b.battery的值(因为期间出现的各种各样的问题)。详情见E.h的第16行和E.cpp的第7,12行。
(2)在main.cpp中新增两行代码第13,14行,并更改原更新里程数使其故意变小,测试如果新增里程数小于之前时的警告信息弹出问题。有警告,可以修改。
4、错误及漏洞
(1)重载<<中的问题:
①错误写法:{out<<```<<endl; out<<```<<endl; return out;}
结果:出现死循环。报错:“operator<<”: 如递归所有控件路径,函数将导致运行时堆栈溢出。查资料得“堆栈溢出”通常出现在 在函数体内反复调用自己 的情况下。应out输出为一句话,其中的换行应用换行符来完成——"\n"。
(在查找资料的时候也发现另一个类似的问题,即“operator>>”: 如递归所有控件路径,函数将导致运行时堆栈溢出。 对应输入时的>>运算符,需要往对象里输入数据,不能向一个const引用的对象里输入,即应该删去const。)
②ostream是个定义了输出流的类,在<<操作重载中,作为友元函数出现。需要using std::ostream;(称为通过使用using关键字来使用c++标准程序库的标识符,using namespace std;所有标识符都有效。其中常见也包括endl,string等等。)
(2)派生类怎么调用私有成员。当基类声明为public时,私有成员仍然是私有,类外部不能直接访问.需要在基类的公有成员设置接口,即返回私有成员值的公有函数接口。string getmaker(){return maker;} 。见car.h中的16-19行。 派生类对象名.getmaker() 调用私有成员maker 见ElectricCar.cpp中的第12行。
(3)分文件编写,友元函数要有声明,如果没有声明则有错误提示:不允许使用不完整类型。A类(ElectricCar类)想直接调用B类(Battery类)私有成员,则在B类中定义friendA。(不要混了)
(4)在派生类中初始化基类对象。调用基类的构造函数。“构造函数名(赋值)”也即“基类名(赋值)”。见ElectricCar.cpp中的第四行。
(5)另外注意的小问题:①带默认形参值的参数与不带的参数的位置顺序。②构造函数中是形参给私有成员赋值,即 私有成员(形参)。③类的成员函数如果有默认形参值,必须写在类的定义中
Part 2重载运算符[]
1、代码
1 #include <iostream> 2 using namespace std; 3 4 #include "arrayInt.h" 5 6 int main() { 7 // 定义动态整型数组对象a,包含2个元素,初始值为0 8 ArrayInt a(2); 9 a.print(); 10 11 // 定义动态整型数组对象b,包含3个元素,初始值为6 12 ArrayInt b(3, 6); 13 b.print(); 14 15 // 通过对象名和下标方式访问并修改对象元素 16 b[0] = 2; 17 cout << b[0] << endl; 18 b.print(); 19 20 system("pause"); 21 22 return 0; 23 }
(突然蹦出空行,不知道怎么删除)
1 #ifndef ARRAY_INT_H 2 #define ARRAY_INT_H 3 4 class ArrayInt{ 5 public: 6 ArrayInt(int n, int value=0); 7 ~ArrayInt(); 8 int & operator[](int a);// 补足:将运算符[]重载为成员函数的声明 9 // ××× 10 void print(); 11 private: 12 int *p; 13 int size; 14 }; 15 16 #endif
1 #include "arrayInt.h" 2 #include <iostream> 3 #include <cstdlib> 4 using std::cout; 5 using std::endl; 6 7 ArrayInt::ArrayInt(int n, int value): size(n) { 8 p = new int[size]; 9 10 if (p == nullptr) { 11 cout << "fail to mallocate memory" << endl; 12 exit(0); 13 } 14 15 for(int i=0; i<size; i++) 16 p[i] = value; 17 } 18 19 ArrayInt::~ArrayInt() { 20 delete[] p; 21 } 22 23 void ArrayInt::print() { 24 for(int i=0; i<size; i++) 25 cout << p[i] << " "; 26 cout << endl; 27 } 28 29 // 补足:将运算符[]重载为成员函数的实现 30 // ××× 31 int & ArrayInt::operator[](int a) 32 { 33 return p[a]; 34 }
2、程序结果
3、有关过程
(1)理解是干什么的:ArrayInt类的对象,有两个参数n(数组大小),和value(数组元素赋的值),构造动态整形数组。并能够通过“对象名[i]”的形式访问该数组的第i+1个元素。
(2)错误编写一:声明:ArrayInt operator[](const ArrayInt &c,int a);
函数实现:{return p[a];}
①[]是单目运算符,且主函数调用的形式是“对象名[整形数]”
②调试报错:main.cpp第17行cout << b[0] << endl;没有与ArrayInt类型相匹配的<<运算符
错误编写二:声明:int operator[](int a);
函数实现:{return p[a];}
①调试报错:main.cpp第16行b[0] = 2;表达式必须为可修改的左值。即返回了常量,但是需要变量。
(3)综上:返回值的类型是个整形变量。翻书才发现上课老师讲过,即“int & operator运算符()”的形式,它返回引用类型。(为了能写在等号左边)
总结:
一股脑看完题会很难受,但其实从一点儿一点儿编起,一块一块的完成,最后还是挺有成就感。
不知道时常依赖VS中红色波浪线好不好,是不是错过了很多问题。
第一遍完成和后来完善,每次看程序查书的感觉都不一样,不断觉着之前苦思的都是傻问题或突然领悟或上课本来就讲过。
此次实验比较满意的是在编写过程中建立了文档专门记录自己各种各样的问题,遇到即记下来,所以可能此次每块的总结会细一些,想以后复习应该也有很大帮助。
互评网址:
1、