C++期末实践程序设计与数组作为参数的注意事项
目录
- 小表弟发来的求助信号
- 要点
- 代码文件
- 头文件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
本文只是给出了模型示例,一般真实项目里面不会这么简单。学习课本可能很难理解后面这两种写法,就请先忽略吧。
C++期末实践程序设计与数组作为参数的注意事项相关推荐
- Java单元测试实践-09.Mockito的Stub参数条件
Java单元测试实践-00.目录(9万多字文档+700多测试示例) https://blog.csdn.net/a82514921/article/details/107969340 1. Mocki ...
- c语言设计由数组,C语言程序设计_07数组.ppt
C语言程序设计_07数组 注意:%S用在SCANF()中控制输入时,输入的字符串不能含有空格或制表符.因为C语言规定,用SCANF()输入字符串是以空格或回车符作为字符串间隔符号:当%S遇到空格或制表 ...
- 数组做参数_ES6 系列:你不知道的 Rest 参数与 Spread 语法细节
Rest 参数与 Spread 语法 在 JavaScript 中,很多内建函数都支持传入任意数量的参数. 例如: Math.max(arg1, arg2, ..., argN) -- 返回入参中的最 ...
- Java 一维数组作为参数和返回值
一维数组作为参数: 传数组的引用 创建数组直接传,本质也是传数组的引用 传null public class Test {//数组作为参数时,可以传递3中形式public void m1(int[] ...
- 【C 语言】数组作为参数退化为指针问题 ( 问题描述 | 从编译器角度分析该问题 | 出于提高 C 语言执行效率角度考虑 | 数组作为参数的推荐方案 )
文章目录 一.问题描述 二.从编译器角度分析该问题 三.数组作为参数的推荐方案 一.问题描述 将 数组 作为 函数参数 , 传递时会 退化为指针 ; 数组的首地址 , 变为指针地址 , 函数中无法判定 ...
- Julia程序设计3 数组2 排序、复制、集合运算、字典
Julia程序设计3 数组2 排序.复制.集合运算.字典 排序 排序算法 查找数组中的元素 复制数组 for语句实现数组的变换与生成 集合运算 字典 创建字典 查阅字典 查看索引 修改字典 添加.删除 ...
- Julia程序设计3 数组1 创建、初始化、属性与访问
Julia程序设计3 数组 创建数组 数组的属性 访问数组中的元素 创建数组 1.按列创建数组:如果用中括号创建数组.元素之间用逗号隔开,创建出来的数组就是列向量的形式:用中括号也可以按列创建多维数组 ...
- js ajax传对象数组参数,JS传递对象数组为参数给后端,后端获取
JS传递对象数组为参数给后端,后端获取. 前端JS代码: var conditons = []; var test1 = new Object(); test1.name="1"; ...
- ajax post提交数组6,jQuery的ajax()、post()方法提交数组,参数[] 问题
当用$.ajax()向后台提交参数时,如果参数中数组的话一般在后台会用List,或Integer[] 等数组对象进行接收. 比如: $.ajax({ type: "POST", u ...
最新文章
- linux shell 读取文件 笔记
- 和lock一起学beego 博客系统开发为例(五)
- python加法怎么写_51NOD 大数加法以及python写法
- LARS 最小角回归算法简介
- c调用python脚本如何获取结果_使用C++调用Python代码的方法详解
- 爬虫推特数据分析的外文文献_13天让你学会爬虫分布式,说到让你做到择推出it届附教程...
- 单元测试02:Open-Cover安装与使用
- 使用AWS Lambda在Go中构建RESTful API
- scala字符串替换_如何在Scala中替换字符串中的正则表达式模式?
- C++常用特性原理解析
- InnerClass内部类
- SQL中实现截取字符串的函数
- 如何利用tcpdump抓包?
- linux下安装redis-cli
- 【算法系列学习】Dijkstra单源最短路 [kuangbin带你飞]专题四 最短路练习 A - Til the Cows Come Home...
- 周立功上位机获取CAN通讯数据解析 V2.0
- 免费SSL证书和付费SSL证书有什么区别?
- Axure8.0使用教程
- PHP在线手册 中文版
- 《SQL进阶教程》学习
热门文章
- dede plus ad js.php,织梦程序中plus文件作用介绍及安全设置
- c# 赋值运算符_C#程序演示赋值运算符的示例
- 史上最全的延迟任务实现方式汇总!附代码(强烈推荐)
- linux内核2.6.35编译过程
- std::optional
- 利用NCO 3.0 调用SAP中的函数
- Win10系统java环境配置
- 快速锁屏电脑快捷键_一学就会的8个电脑快捷键一定要掌握!堪称“高效”神器...
- tp获取referer里的域名_thinkPHP的redirect跳转和referer使用的问题
- java接口如何接受语音参数_Java 是如何优雅地实现接口数据校验的?