目录
- 小表弟发来的求助信号
- 要点
- 代码文件
- 头文件Student.h
- 源文件Student.cpp
- main.c
- 执行结果
- c++数组特性以及数组做形参的特点
- 数组试验
- 数组特殊性质
- 不允许拷贝和赋值
- 数组是通常被转化成指针使用
- 数组形参
- 多种写法
- 代理模式
- MVC模式
小表弟发来的求助信号
并补充说要5种写法才算过关。
要点
先不说要几种写法,来说说这个实践的目的,也就是我们常常说的需求。
从题目本身说明是 面向对象程序设计 的考察;源文件和头文件分开,进一步说明是C++ 面向对象程序设计,同时也是C++程序设计的标准工程管理方式(头文件、源文件各自在一个文件中),只是在学习的时候往往一个文件将所有的代码放一个文件中。
类定义,实际上已经指明了类的属性以及需要具备的函数:
成绩要求用数组,是对数组的考察:
- 数组的初始、赋值、传递,别小看,容易掉坑里。
- 用自己的真实学号,就要求结合实际,可选用string或long类型
- 姓名已经明确是string,但对要求用中文其实是命题上的错误,不知道外国留学生是否都有一个中文姓名,唯一的提醒就是中文字符的编码问题,但仅仅在这个作业中还体现不了字符编码。
- const 自然是程序中要有用到const的地方
- 要求有构造函数,是对构造函数的考察
- 求平均数主要在于返回值和平均数的计算
- 打印学习成绩,和6一样,可以封装,分装的基础上再代理,后者以mvc的模式定义实体来实现,当然,这对于学生来说,可能稍显超标。
代码文件
不啰嗦了,先上个代码,不然看不到希望。
头文件Student.h
内容如下:
#ifndef STUDENT_H_
#define STUDENT_H_
#include <iostream>
using namespace std;
class Student{
private:string name;string no;float scores[3]={0,0,0};
public:Student(const string name,const string no,const float scores[3]);~Student();float Average();void SetName(const string name);void SetNo(const string no);void SetScore(const float scores[3]);void Print();
};
#endif
源文件Student.cpp
内容如下:
#include <iostream>
#include "Student.h" //不行的话,这里换成<Student.h>
using namespace std;Student::Student(const string name,const string no,const float scores[3])
{this->name = name;this->no = no;SetScore(scores);
};Student::~Student(){};
float Student::Average()
{float total = 0;if(this->scores){for(int i = 0;i < 3;i++){total += scores[i];}}return total/3;
};void Student::SetName(const string name)
{this->name = name;
};void Student::SetNo(const string no)
{this->no = no;
};void Student::SetScore(const float scores[3])
{for(int i = 0; i < 3 ; i++){this->scores[i] = *scores;scores++;}
};void Student::Print()
{cout<<"姓名:"<<name<<endl;cout<<"学号:"<<no<<endl;cout<<"成绩"<<endl;cout<<"语文:"<<scores[0]<<endl;cout<<"数学:"<<scores[1]<<endl;cout<<"英语:"<<scores[2]<<endl;cout<<"平均成绩:"<<Average()<<endl;
};
main.c
程序入口,根据需要调用
#include <iostream>
#include "Student.h" //不行的话,这里换成<Student.h>
using namespace std;
int main()
{float scores[] = {78,88,98};Student stu("john","8989898989",scores);stu.Print();return 0;
}
代码在线测试
执行结果
$g++ -o main *.cpp
$main
姓名:john
学号:8989898989
成绩
语文:78
数学:88
英语:98
平均成绩:88
至此代码可以拿走了。
c++数组特性以及数组做形参的特点
这里主要说明一下题目要求使用数组,实际上呢是对数组的一个特点、特性、指针做一个真实的巩固。
数组试验
#include<iostream>
using namespace std;int GetSize(int data[]) {return sizeof(data);//特别注意这里
}
int main() {int array[] = {1,2,3,4,5};int arraySize = sizeof(array);int *pointer = array;int pointerSzie = sizeof(pointer );int size = GetSize(array);cout<<"数组大小sizeof "<<arraySize <<" 指针sizeof "<<pointerSzie <<" 形参sizeof "<<size<<endl;return 0;
}
执行结果
$g++ -o main .cpp
main.cpp: In function ‘int GetSize(int)’:
main.cpp:5:23: warning: ‘sizeof’ on array function parameter ‘data’ will return size of ‘int*’ [-Wsizeof-array-argument]
return sizeof(data);//特别注意这里
^
main.cpp:4:22: note: declared here
int GetSize(int data[]) {
^
$main
数组大小sizeof 20 指针sizeof 8 形参sizeof 8
- 数组大小sizeof 20 指针sizeof 8 形参sizeof 8: 大小都是指字节(byte),其中一个int 占4个字节,数组一共5个元素,因此sizeof(array) 是20;
- int型的指针,是一个内存地址,内存地址一般与计算机系统有关,与 指向的类型没有关系(例如 string *str ,str的sizeof也是8),如果是32位系统,内存寻址大小是4个字节(32bit),64位系统,内存寻址大小8个字节(64bit)。上面结果足以表明测试站点后端的执行系统是64位的,sizeof(pointer )是8;
- 刚好有个warning ‘data’ will return size of ‘int*’ [-Wsizeof-array-argument],说明 形参数组 将会作为对应类型的指针使用,是指针,sizeof自然就是8。
数组特殊性质
不允许拷贝和赋值
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
int a[] = {0,1,2}; // 含有三个整数的数组
int a2[] = a; // 错误:不允许使用一个数组初始化另一个数组
a2 = a;
简而言之就是数组之间不能用等号来做赋值操作
数组是通常被转化成指针使用
在C++语言中,指针和数组有非常紧密的联系。使用数组的时候编译器一般会把它转换成指针,通常情况下,使用取地址符来获取指向某个对象的指针,取地址符可以用于任何对象。
数组的元素也是对象,对数组使用下标运算符得到该数组指定位置的元素。再对该元素使用取地址符就能得到指向该元素的指针:
string nums[] = {"one", "two", "three"}; // 数组元素是string对象
string *p = &nums[0];
string *p1 = nums;
在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针 。
数组形参
因为不能拷贝数组,所以我们无法以 值传递 的方式使用数组参数。因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
虽然不能以值传递的方式传毒数组,但是可以把形参写成类似数组的形式:
//形式不同,但这三个print结果是等价的
//每个函数都有一个const int*类型的形参
void print(const int*);
void print(const int[]);
//这里的维度表示我们期望数组含有多少元素,实际不一定就是10
void print(const int[10]);
所以,数组作为形参的函数也必须确保使用数组时不会越界。越界了之后自然就是不是我们预期的结果。
多种写法
前面的写法基于最朴实的面向对象和封装的思想。如果一定还要多种写法,那就变化一下。
代理模式
这种只是代理方式,因此只需要修改main文件
#include <iostream>
#include "Student.h" //不行的话,这里换成<Student.h>
using namespace std;float average(Student *stu){return stu->Average();
}void print(Student * stu){stu->Print();
}int main() {float scores[] = {78,88,98};Student stu = Student("john","8989898989",scores);cout<<"平均成绩:"<<average(&stu)<<endl;cout<<"打印:"<<endl;print(&stu);return 0;
}
输出结果:
$g++ -o main *.cpp
$main
平均成绩:88
打印:
姓名:john
学号:8989898989
成绩
语文:78
数学:88
英语:98
平均成绩88
纳里,这有什么不一样吗,反而还多了两个函数。是的就是多了两个函数,在面向过程的思维前提下,这反而是增加了代码量,但这在实际应用当中很有用,有多个Student的子类的话,这就体现出作用了。
MVC模式
这种模式下 Student 只定义属性,并且有Get和Set函数。没有Print 、Averager函数,这些函数是定义在类之外的,此处还是写在main 之前,合理做法应该是另外再定义一个类。
Student头文件
#include<iostream>
using namespace std;
class Student{
private:string name;string no;float scores[3]={0,0,0};
public:Student(const string name,const string no,const float scores[3]);~Student();void SetName(const string name);string GetName();void SetNo(const string no);string GetNo();void SetScore(const float scores[3]);float* GetScores() ;//float Average(); 此类不计算了//void Print(); 此类也不做打印
};
Student源文件
#include<iostream>
using namespace std;
Student::Student(const string name,const string no,const float scores[3]){this->name = name;this->no = no;SetScore(scores);
};Student::~Student(){
};void Student::SetName(const string name){this->name = name;
};string Student::GetName(){return this->name;
}
void Student::SetNo(const string no){this->no = no;
};string Student::GetNo(){return this->no;
}float * Student::GetScores(){return scores;
}void Student::SetScore(const float scores[3])
{for(int i = 0; i < 3 ; i++){this->scores[i] = *scores;scores++;}
};
main.cpp
#include <iostream>
#include "Student.h" //不行的话,这里换成<Student.h>
using namespace std;float average(Student *stu){return (stu->GetScores()[0]+stu->GetScores()[1]+stu->GetScores()[2])/3;
};void print(Student * stu){cout<<"姓名:"<<stu->GetName()<<endl;cout<<"学号:"<<stu->GetNo()<<endl;cout<<"成绩"<<endl;cout<<"语文:"<<stu->GetScores()[0]<<endl;cout<<"数学:"<<stu->GetScores()[1]<<endl;cout<<"英语:"<<stu->GetScores()[2]<<endl;cout<<"平均成绩"<<average(stu)<<endl;};int main() {float scores[] = {78,88,98};Student stu = Student("john","8989898989",scores);cout<<"平均成绩:"<<average(&stu)<<endl;cout<<"打印:"<<endl;print(&stu);return 0;
}
输出结果:
$g++ -o main *.cpp
$main
平均成绩:88
打印:
姓名:john
学号:8989898989
成绩
语文:78
数学:88
英语:98
平均成绩88
本文只是给出了模型示例,一般真实项目里面不会这么简单。学习课本可能很难理解后面这两种写法,就请先忽略吧。