1-K1
input
输入两个正整数 A 和 B。(0≤A,B≤100)
Output
依次输出A mod B、A÷B、 A+B、A−B,每个结果占一行,注意最后还有一个换行符。当 B=0 时,输出的 A mod B、A÷B 的结果也为0。
我的
#include<iostream>//标准输入输出库有三个对象:cin,cout,cerr
//定义了一些无参的操纵符
#include<iomanip>
using namespace std;
int main(){
int a,b,d;
cin >> a >> b;//cin是标准输入流对象
//“<<”重载为输出运算符,表达式返回左操作数的引用,是左值
float c;
if(b==0){
c=0;
d=0;
cout << d << endl << c << endl << a+b << endl << a-b << endl;
}else{
c=(float)a/b;
cout << a%b << endl << setprecision(1) << c << endl << a+b << endl<< a-b << endl;
}
return 0;
}
iostream里的操纵符
1.置基:
dec 置基数为10 相当于"%d"
hex 置基数为16 相当于"%X"
oct 置基数为8 相当于"%o"
fixed,scientific,更改浮点数格式化
补充知识:#include<iomanip>中的iomanip的一些用法
2.设宽
setprecision( n ) 设显示有效数字为n位
setw( n ) 保证输出宽度为n
答案
#include <iostream>
using namespace std;
//命名空间,不会的语法多看看cpppreference作为参考
int main() {
int a, b;
cin >> a >> b;
if(b) cout << a % b << endl;//可直接输出算式的结果
else cout << 0 << endl;
if(b) cout << (double)a / b << endl;//注意隐式转换
else cout << 0 << endl;
cout << a + b << endl << a - b << endl;
return 0;
}
简洁明了,没有花里胡哨
注意:C++比C严格,const指针值必须赋给const指针变量,要么隐式转换,要么间接拷贝值
1-K2
Input
输入两个正整数 A 和 B。(0≤A,B≤100)
Output
依次输出A×B、A÷B、 A+B、A−B,每个结果占一行,注意最后还有一个换行符。当 B=0 时,输出的 A÷B 结果也为0。
#include<iostream>
#include<iomanip>
using namespace std;
int main(){
int a,b;
cin >> a >> b;
float c;
if(b==0){
c=0;
cout << a*b << endl << c << endl << a+b << endl << a-b << endl;
}else{
c=(float)a/b;
cout << a*b << endl << setprecision(1) << c << endl << a+b << endl<< a-b << endl;
}
return 0;
}
答案
2-H1
Description
练习分别通过值,引用,指针来传递参数。
面向对象编程,分块化思想
//main.cpp
#include<iostream>
#include<string>
#include "swap.h"
using namespace std;
int main(){
int a, b;
cin >> a >> b;
cout << "Original Value" << endl;
cout << a << " " << b << endl;
cout << "Swap by using value" << endl;
use_value(a, b);
cout << a << " " << b << endl;
cout << "Swap by using pointer" << endl;
use_pointer(&a, &b);
cout << a << " " << b << endl;
cout << "Swap by using reference" << endl;
use_reference(a, b);
cout << a << " " << b << endl;
return 0;
}
//swap.h
void use_value(int a, int b);
void use_pointer(int* a, int* b);
void use_reference(int& a, int& b);
//swap.cpp
#include "swap.h"//一定要有
//其他的头文件或者命名空间什么时候需要再加上
void use_value(int a, int b){
int t = a;
a = b;
b = t;
}
void use_pointer(int *a, int* b){
int t = *a;
*a = *b;
*b = t;
}
void use_reference(int& a, int& b){
//注意在实参中也不需要&
//只在函数形参中出现,其他地方和原来的一样
//左值引用的声明:type &别名(=左值表达式)
//int &ref_a=a;ref_a是引用,它实际上与a是同一个变量
//引用和指针都可以实现使一个函数向调用者返回多个数值
int t = a;
a = b;
b = t;
}
引用,特别注意!不是对象,不必占用存储,不存在引用的数组,不存在指向引用的指针,不存在引用的引用
规定:返回值使用引用
#include<iostream>
#include<math.h>
using namespace std;
double& f(double x){
static double y;
y=sin(x);
return y;
}
//会默认创建返回值的引用
int main(){
double a=3.14/6;
double y;
y=f(a);
cout<<"y = "<<y<<endl;
return 0;
}
//using namespace std;
//double& f(double x){
// double y;
// y=sin(x);
// return y;
//}
//int main(){
// double a=3.14/6;
// double y;
// y=f(a);
// cout<<"y = "<<y<<endl;
// return 0;
//}
//[Warning] reference to local variable 'y' returned [-Wreturn-local-addr]
//函数的返回值为函数内部定义变量的引用,但函数在调用完毕后,函数内部定义的变量空间被释放,无法访问,从而造成的错误
//改正方法一:给返回变量定义加上static限定符,保证在函数调用完后不释放空间
//改正方法二:去掉返回值的引用,保证函数外部无法访问返回值的空间,只能得到其内容
补充,c++语言内存
(1) 堆(heap) 编译器不用去管,可以使用new和malloc来申请内存,但必须在不使用的时候使用delete和free释放掉。
(2) 栈(stack) 存放着局部变量,函数参数,局部常量。
(3) 静态存储区 主要存储全局静态变量,局部静态变量,全局变量,以及虚函数表。
(4) 常量存储区 主要保存全局常量,函数指针,常量数组。
(5) 代码区 就是存放代码的地方
低地址->高地址:正文段,数据段(静态数据,全局数据),堆,栈
2-H2
Description
编写一个程序去读入包含 name
(std::string
), code
(std::string
),和 cost
(double
) 信息的书本清单,然后产生一个满足以下格式要求的三列输出:
name
和code
都是 左对齐的;cost
是两位数精度并且是右对齐的,不足两位精度的用0补齐;name
,code
andcost
的字段宽度分别是15, 15, 10。
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main() {
int N;
string name, code;
double cost;
cin >> N;
while(N--) {
cin >> name >> code >> cost;
cout << setw(15) << left << name <<
setw(15) << left << code <<
setw(10) << right << setprecision(2) << fixed << cost << endl;
}
}
左对齐输出
1.使用printf,%-8d是把a整个输出的宽度设为8,左对齐输出。
(在C++中,cstdio和stdio.h这两个标准输入输出头文件里面都有printf函数)
2.#include<iomanip>
std::left
setw(宽度)
fixed:消除浮点数的科学计数法
3-K1
类:class数据类型,关于数据成员和函数成员的描述
string s2="C++";不是赋值运算,是初始化
目前需要掌握的字符串操作
length:C++中string.length()返回类型是size_t,可以简单地认为是unsigned int 类型,即无符号类型,如果不经过转换就拿它和有符号类型进行比较,很容易发生错误。
c_str:const char *c_str();c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同。
这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。
注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针。
find:string的find()函数用于找出字母在字符串中的位置。find(str,position),str:是要找的元素position:字符串中的某个位置,表示从从这个位置开始的字符串中找指定元素(不填第二个参数,默认从字符串的开头进行查找)返回值为目标字符的位置(第一个字符位置为0),当没有找到目标字符时返回npos
rfind: 逆向查字符或字符串,若查找成功,则返回逆向查到的第一个字符下标或第一个字符串首字符的下标;若查找失败,无法返回正确的下标。逆向查到的第一个字符或第一个字符串也就是正向的最后一个。rfind()函数的返回值为无符号整数类型。
append:
- 直接添加另一个完整的字符串:如str1.append(str2);
- 添加另一个字符串的某一段子串:如str1.append(str2, 11, 7);
- 添加几个相同的字符:如str1.append(5, ‘.’);注意,个数在前字符在后.上面的代码意思为在str1后面添加5个"."
insert:
- string& insert (size_t pos, const string& str);
- 说明:在源字符串下标为pos处插入string型字符串str。
- string& insert (size_t pos, const string& str, size_t subpos, size_t sublen = npos);
- 说明:在源字符串下标为pos处插入string型字符串str中以下标处为subpos开始的sublen个字符组成的子字符串。
- string& insert (size_t pos, size_t n, char c);
- 说明:在源字符串下标为pos处插入n个字符c。
erase:string.erase(pos,n) //删除从pos开始的n个字符 ;string.erase(0,1); 删除第一个字符string.erase(pos) //删除pos处的一个字符(pos是string类型的迭代器)string.erase(first,last) //删除从first到last中间的字符(first和last都是string类型的迭代器)
clear:
- void clear() noexcept;
- 说明:将字符串的内容清空,让源字符串成为一个空字符串(长度为0个字符)。
- string& erase (size_t pos = 0, size_t len = npos);
- 说明:删除源字符串以下标为pos开始的len个字符,返回修改后的字符串。
substr:
substr(size_type _Off = 0,size_type _Count = npos)
参数:_Off
——所需的子字符串的起始位置。字符串中第一个字符的索引为 0,默认值为0。_Count
——复制的字符数目返回值
——一个子字符串,从其指定的位置开始
compare:str1.compare(str2);如果相等则输出为0,不等则输出为-1
replace:将源字符串中某个字符串只替换了一次,string类并没有实现对于源字符串中的某个字符串全部替换
string str_line("I love compile program with visual studio.");
cout << str_line.replace(str_line.find('e'), 1, "#") << endl;
// 答案: I lov# compile program with visual studio.
copy:
函数原型:size_t copy (char* s, size_t len, size_t pos = 0) const;
函数功能:copy函数的作用是从string对象中取出若干字符存放到数组s中。其中,s是字符数组,n表示要取出字符的个数,pos表示要取出字符的开始位置。
函数参数:参数s:字符数组,用来存放从string对象取出的字符。参数len:取出的字符个数。参数pos:要取出的字符在string对象中的开始位置。
函数的返回值:函数返回取出字符的个数。
swap:str1.swap(str2);//把 str1 与 str2 交换
stoi,stod,暂时没有遇到过
函数重载:允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数的重载。
const int x不可以用来初始化array容器,const int x=5,可以用来初始化array容器
只读:const
常量:constexpr
C++11 新增了一种循环:基于范围(range-based)的 for 循环。这简化了一种常见的循环任务:对数组(或容器类,如 vector 和 array)的每个元素执行相同的操作,如下例所示:
double prices[5] = {4.99, 10.99, 6.87, 6.47, 8.88};
for (double x : prices)
std::cout << x << std::endl;
其中,x 最初表示数组 prices 的第一个元素。显示第一个元素后,不断执行循环,而 x xx 依次表示数组的其他元素。因此,该循环可以用来显示数组中的每个值。
要修改数组的元素,需要使用不同的循环变量语法:
for (double &x : prices)
x = x * 0.80; //20% off sale
符号 & 表明 x 是一个引用变量,能让接下来的代码能够修改数组的内容,而第一种语法不能。
也可以拓展到auto,for(auto i:v) cout<<i<<" ";
sizeof(bool)的值由实现定义,而且不一定是1
一个有用的函数:DayInMonth
int DaysInMonth(int mo,int yr)
{
switch(mo)
{
case 1:case 3:case 5:case 7:case 8:case 10:case 12:
return 31;
case 4:case 6:case 9:case 11:return 30;
case 2:
if((yr%4==0&&yr%100!=0)||yr%400==0) return 29;
else return 28;
}
}
Problem Description
请设计类 Dog
, 它包含下面的公有成员函数:
int get_age() const
string get_name() const
void set_age(int age)
//framework.cpp
#include <iostream>
#include <string>
using namespace std;
#include "source.cpp"
void print_Dog(const Dog & Dog) {
cout << "This is my Dog:" << endl;
cout << Dog.get_name() << endl;
cout << Dog.get_age() << endl;
}
int main() {
int age;
string name;
cin >> name >> age;
Dog Dog;
Dog.set_name(name);
Dog.set_age(age);
print_Dog(Dog);
}
//source.cpp
#include <iostream>
#include <string>
using namespace std;
class Dog
{
int age;
string name;
public:
string get_name() const {
return name;
}
int get_age() const {
return age;
}
void set_name(const string & n) {
name = n;
}
void set_age(int a) {
age = a;
}
};
3-K2
Description
实现一个point
类,它具有以下特征:
point
有一个静态数据成员:count
,记录了一共创建了多少个point
对象,初始化为0point
有两个基本属性x
,y
分别记录了x和y轴的坐标,x,y的初始值为0point
有一个方法,judge
可以判断三个点是否在一条直线上point
可以打印出点的坐标(x,y)
//main.cpp
#include <iostream>
#include "point.h"
using namespace std;
int main ( )
{
double x,y;
cin>>x>>y;
point p1(x,y);
p1.print();
cout<<point::count<<endl;
cin>>x>>y;
point p2(x,y);
cin>>x>>y;
point p3(x,y);
if(p1.judge(p2,p3)){
cout<<"True"<<endl;
}
else{
cout<<"False"<<endl;
}
cout<<point::count<<endl;
return 0;
}
//point.h
#pragma once//防止重复
class point{
public:
point(double x=0,double y=0){
this->x=x;
this->y=y;
count++;
}
static int count;//静态成员变量
void print();
bool judge(const point &,const point &);
private:
double x;
double y;
};
//point.cpp
#include <iostream>
#include "point.h"
int point::count=0;//只能在类外定义,在类的实现文件中再次声明
void point::print() {
std::cout<<'('<<x<<','<<y<<')'<<std::endl;
}
bool point::judge(const point &p1,const point &p2){
double dx1=p1.x-x;
double dx2=p2.x-x;
double dy1=p1.y-y;
double dy2=p2.y-y;
if(dx1==0&&dx2==0){
return true;
}
else if(dx1==0||dx2==0){
return false;
}
else if(dy1/dx1==dy2/dx2){
return true;
}
else{
return false;
}
}
static:
静态成员变量存储在全局数据区。static成员变量的内存空间既不是在声明类时分配,也不是在创建对象时分配,而是在初始化时分配。静态成员变量必须初始化,而且只能在类体外进行。否则,编译能通过,链接不能通过。
静态成员是类的组成部分但不是任何对象的组成部分,有静态生存期,是类的所有对象共享的存储空间,不是通过构造函数进行初始化,通常是在类的实现文件中再声明一次,而且此时不用statics修饰。
静态成员函数不属于任何对象,没有this指针,不能直接访问非静态数据成员,只能直接访问类的静态数据成员
3-H1
有默认实参的构造函数的声明和实现:
class Line{
public:
Line(double len=0);
private:
double length;
};
Line::Line(double len){
//这里不要=0的操作
length=len;
}
/*或者这样,直接在类内定义
class Line{
public:
Line(double len=0){
length=len;
}
private:
double length;
};
*/
Description
设计一个名为Rectangle
的类来表示矩形。该类包含:
- 两个名为
width
和height
的double
数据字段,用于指定矩形的宽度和高度。width
和height
的默认值均为1。 - 创建无参数构造函数
Rectangle
,width
和height
默认值均为1。 - 创建具有指定宽度和高度的
Rectangle
的构造函数。 - 所有数据字段的访问器和赋值函数。(
getWidth()
,getHeight()
、setWidth()
和setHeight()
) getArea()
函数,返回此“矩形”的区域。getPerimeter()
函数,用于返回周长。
与前文有相似,按照要求写即可
3-H2
Description
定义复数类Complex
。该类包含:
- 两个名为
real
和image
的整数变量,分别表示实部和虚部,real
和image
的默认值均为0。 c1.add(c2)
表示复数c1
和c2
相加,结果保存在c1
之中c1.mul(c2)
表示复数c1
和c2
相乘,结果保存在c1
之中c1.show()
显示c1
的结果
//main.cpp
#include <iostream>
#include "complex.cpp"
using namespace std;
int main() {
int a, b;
cin >> a >> b;
int c, d;
cin >> c >> d;
Complex c1(a, b);
Complex c2(c, d);
cout << "Function show:" << endl;
c1.show();
cout << "Function show:" << endl;
c2.show();
cout << "Function add:" << endl;
c1.add(c2);
cout << "Function show:" << endl;
c1.show();
cout << "Function mul:" << endl;
c1.mul(c2);
cout << "Function show:" << endl;
c1.show();
return 0;
}
//complex.cpp
#include <iostream>
using namespace std;
class Complex {
private:
long long real, image;
public:
Complex(int _real = 0, int _image = 0) {
real = _real;
image = _image;
}
void show() {
cout << "real part: " << real << endl;
cout << "imaginary part: " << image << endl;
}
void add(const Complex& c) {
real += c.real;
image += c.image;
cout << "Add done!" << endl;
}
void mul(const Complex& c) {
long long tmp_real = real * c.real - image * c.image;
long long tmp_image = real * c.image + image * c.real;
real = tmp_real;
image = tmp_image; //学好数学很重要
cout << "Mul done!" << endl;
}
};
3-H3(重点)
Problem Description
以下是一个简单的信息系统的例子。这个例子中缺少Cat
类和CatFamily
类(假设CatFamily
中至多有100只猫)。
请阅读C++主程序,并了解类你需要实现一些什么。
//framework.cpp
#include <iostream>
#include <string>
using namespace std;
//代码很多,从主程序开始看
#include "source.cpp"
int main_menu() {
cout << "1. Add a cat" << endl;
cout << "2. Remove a cat" << endl;
cout << "3. Find a cat" << endl;
cout << "4. Print all cats" << endl;
cout << "0. Exit this game" << endl;
int choice;
cin >> choice;
return choice;
}
void add_a_cat(CatFamily & cat_family) {
string cat_name;
int cat_age;
cin >> cat_name >> cat_age;
if (cat_family.add_a_cat(cat_name, cat_age))
//存在函数 int add_a_cat(string cat_name, int cat_age);
cout << "Successfully added cat: " << cat_name << endl;
else
cout << "Fail to add cat: " << cat_name << endl;
}
void remove_a_cat(CatFamily & cat_family) {
string cat_name;
cin >> cat_name;
if (cat_family.remove_a_cat(cat_name))
//存在函数 int remove_a_cat(string cat_name, int cat_age);
cout << "Successfully removed cat: " << cat_name << endl;
else
cout << "Fail to remove cat: " << cat_name << endl;
}
void find_a_cat(CatFamily & cat_family) {
string cat_name;
cin >> cat_name;
if (cat_family.has_cat(cat_name)) {
//存在函数 int has_cat(string cat_name);
cout << "Here is your cat: "
<< cat_family.get_cat(cat_name).cat_name
//存在函数 Cat get_cat(string cat_name);
<< " of " << cat_family.get_cat(cat_name).cat_age << "years old" << endl;
}//通过.运算符知道,get_cat函数返回其他的类,该类包含cat_age这个共有成员
else
cout << "No such cat: " << cat_name << endl;
}
void print_all_cats(CatFamily & cat_family) {
cout << "We have these cats:" << endl;
for (int i = 0; i < cat_family.number_of_cats(); ++ i) {
string cat_name = cat_family.get_nth_cat(i);
//存在函数 string get_nth_cat(int);
cout << '\t' << cat_family.get_cat(cat_name).cat_name
<< " of " << cat_family.get_cat(cat_name).cat_age << "years old" << endl;
} //通过.运算符知道,get_cat函数返回其他的类,该类包含cat_age这个共有成员
}
int main() {
CatFamily cat_family;//需要创建一个类:CatFamily
while (true) {
int choice = main_menu();
switch (choice) {
case 0: cout << "See you" << endl; exit(0);
case 1: add_a_cat(cat_family); break;
case 2: remove_a_cat(cat_family); break;
case 3: find_a_cat(cat_family); break;
case 4: print_all_cats(cat_family);
}
}
}
不如创建两个类,一个包含name,age(Cat
一个包含Cat的类组(Cat_family
//source.cpp
#include<iostream>
#include<string>
using namespace std;
class Cat {
public:
string cat_name;
int cat_age;
};
class CatFamily
{
Cat cats[100];//与创建数组语法相似
int count;
int _index(const string & name) const {
for (int i = 0; i < count; ++ i)
if (cats[i].cat_name == name) return i;
return -1;
}//找到该猫咪的位置
public:
CatFamily() {
count = 0;
}//无参构造函数默认初始化
int number_of_cats() const {
return count;
}//类的成员函数后面加 const,表明这个函数不会对这个类对象的非静态数据成员作任何改变。
string get_nth_cat(int num) const {
return cats[num].cat_name;
}//cats[num]每一个都是Cat类型,可以调用自己的类成员
bool add_a_cat(const string & name, int age) {
int i = _index(name);
if (i != -1)
return false;
//如果已经存在不能添加
Cat cat;
cat.cat_name = name;
cat.cat_age = age;
cats[count] = cat;
++ count;
return true;
}
bool remove_a_cat(const string & name) {
int i = _index(name);
if (i == -1)
return false;
cats[i] = cats[count - 1];
-- count;
return true;
}
bool has_cat(const string & name) const {
int i = _index(name);
//当需要重复使用且不需要被调用时不妨设为私有成员
return (i != -1);
}
Cat get_cat(const string & name) const {
//函数的返回值是什么要在原函数中实时分析
int i = _index(name);
return cats[i];
}
};
当成员函数参数与成员数据重名时,必须使用this访问成员数据
4-K1(类实现示范)
Desrciption
根据头文件Date.h
完善类Date
。
C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
构造函数中指定的数据成员初始化顺序?
//main.cpp
#include "Date.h"
#include <iostream>
using namespace std;
int main()
{
DATE date1;
int tmp;
date1.Print();
date1.Increment();
date1.Print();
int year, month, day;
cin >> year >> month >> day;
DATE date2( year, month, day );
date2.Print();
date2.Decrement();
date2.Print();
int tmp_y, tmp_m, tmp_d;
tmp_y = date2.getYear();
tmp_m = date2.getMonth();
tmp_d = date2.getDay();
DATE date3(tmp_y, tmp_m, tmp_d);
date3.Print();
return 0;
}
//Date.h
#ifndef FUNCTION_H_INCLUDED
#define FUNCTION_H_INCLUDED
class DATE
{
public:
DATE();
DATE(int year, int month, int day);
~DATE();
int getMonth() const;
int getDay() const;
int getYear() const;
void Print() const;
void Increment();
void Decrement();
private:
int month;
int day;
int year;
};
#endif // FUNCTION_H_INCLUDED
//保证只使用一次
//Date.cpp
#include "Date.h"
#include <iostream>
using namespace std;
int DaysInMonth( int, int );
DATE::DATE()
{
year = 1970;
month = 1;
day = 1;
}
DATE::DATE(int year, int month, int day)
{
this->year = year;
this->month = month;
this->day = day;
}
DATE::~DATE()
{
cout << "The DATE CLASS will be destroyed." << endl;
}
int DATE::getMonth() const
{
return month;
}
int DATE::getDay() const
{
return day;
}
int DATE::getYear() const
{
return year;
}
void DATE::Print() const
{
switch (month)
{
case 1 :
cout << "January";
break;
case 2 :
cout << "February";
break;
case 3 :
cout << "March";
break;
case 4 :
cout << "April";
break;
case 5 :
cout << "May";
break;
case 6 :
cout << "June";
break;
case 7 :
cout << "July";
break;
case 8 :
cout << "August";
break;
case 9 :
cout << "September";
break;
case 10 :
cout << "October";
break;
case 11 :
cout << "November";
break;
case 12 :
cout << "December";
break;
}
cout << ' ' << day << ", " << year << endl;
}
void DATE::Increment()
{
day++;
if (day > DaysInMonth(month, year))
{
day = 1;
month++;
if (month > 12)
{
month = 1;
year++;
}
}
}
//考虑到跨年和跨月
void DATE::Decrement()
{
day--;
if ( day == 0 )
{
if( month == 1 )
{
day = 31;
month = 12;
year--;
}
else
{
month--;
day = DaysInMonth( month, year );
}
}
}
int DaysInMonth( int mo, int yr )
{
int computedDay = 1; // intialize with a default value
switch (mo)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
computedDay = 31;
break;
case 4: case 6: case 9: case 11:
computedDay = 30;
break;
case 2:
if ((yr % 4 == 0 && yr % 100 != 0) ||yr % 400 == 0)
computedDay = 29;
else
computedDay = 28;
break;
}
return computedDay;
}//简化的打表法
4-K2
Description
根据头文件Person.hpp
完善Person
类
//main.cpp
#include <iostream>
#include "Person.hpp"
using namespace std;
void displayPerson(Person &person1, Person &person2)
{
cout << "person1 id: " << person1.getId() << endl;
cout << "person1 birthday: ";
person1.getBirthDate() -> Print();
cout << "person2 id: " << person2.getId() << endl;
cout << "person2 birth year: ";
person2.getBirthDate() -> Print();
}
int main()
{
int id, year, month, day, year2;
cin >> id >> year >> month >> day;//支持持续输入
Person person1(id, year, month, day);
cout << "number of Person objects: " << Person::getNumberOfObjects() << endl;
Person person2(id, year, month, day);
cout << "number of Person objects: " << Person::getNumberOfObjects() << endl;
cout << "After creating person1 and person2" << endl;
displayPerson(person1, person2);
cout << (person1.getBirthDate() == person2.getBirthDate()) << endl;
cin >> year2;
person2.getBirthDate() -> setYear(year2);//person2.getBirthDate()是指针
cout << "After modifying person2's birthDate" << endl;
displayPerson(person1, person2);
Person* p_person3 = &person1;
cout << "number of Person objects: " << Person::getNumberOfObjects() << endl;
cout << (p_person3->getBirthDate() == person1.getBirthDate()) << endl;
{
Person person5(id, year, month, day);
cout << "number of Person objects: " << Person::getNumberOfObjects() << endl;
}
cout << "number of Person objects: " << Person::getNumberOfObjects() << endl;
p_person3 = NULL;
cout << "number of Person objects: " << Person::getNumberOfObjects() << endl;
return 0;
}
//person.hpp
class Date
{
public:
Date();
Date(int year,int month,int day);
int getYear();
void setYear(int year);
int getMonth();
int getDay();
void setMonth(int month);
void setDay(int day);
void Print();
private:
int year;
int month;
int day;
};
class Person
{
public:
Person(int id, int year, int month, int day);
~Person();
int getId();
Date* getBirthDate() const;
static int getNumberOfObjects(); //return the number of Person objects
private:
int id;
Date* birthDate;
static int numberOfObjects; //count the number of Person objects
//需要在类外声明
};
静态函数:(在函数的返回类型前面加上关键字static)
在类中,static 可以声明静态成员函数。普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。
静态成员:
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
//Person.cpp
#include <iostream>
#include "Person.hpp"
using namespace std;
Date::Date(){}
Date::Date(int year, int month, int day)
{
this->year = year;
this->month = month;
this->day = day;
}
int Date::getYear()
{
return year;
}
void Date::setYear(int year)
{
this->year = year;
}
int Date::getMonth()
{
return month;
}
int Date::getDay()
{
return day;
}
void Date::setMonth(int month)
{
this->month = month;
}
void Date::setDay(int day)
{
this->day = day;
}
void Date::Print()
{
cout << year << " " << month << " " << day << endl;
}
Person::Person(int Id, int year, int month, int day)
{
id = Id;
birthDate = new Date(year, month, day);
//开辟一个新的空间,用year, month, day初始化
numberOfObjects++;
}
Person::~Person()
{
delete birthDate;
birthDate=NULL;
numberOfObjects--;
}
int Person::getId()
{
return id;
}
Date *Person::getBirthDate() const
{
return birthDate;//返回一个指针
}
int Person::getNumberOfObjects()
{
return numberOfObjects;
}
int Person::numberOfObjects = 0;//在类外初始化静态变量
4-H1
动态对象的使用:
申请数组:没有初始化列表调用无参构造(默认构造一般是不确定值,常数初始化列表,后面补零;长度小于初始化序列长度,抛出异常)
指针=new 类型名;
指针=new 类型名(初始化参数);
指针=new 类型名【数组长度】;
在C++语言中,我们经常会使用new给一个对象分配内存空间,而当内存不够会出现内存不足的情况。C++提供了两中报告方式:
1、抛出bad_alloc异常来报告分配失败;(报错)
2、返回空指针,而不会抛出异常。
C++为什么会采用这两种方式呢?这主要是由于各大编译器公司设计C++编译器公司的结果,因为标准C++是提供了异常机制的。例如,VC++6.0中当new分配内存失败时会返回空指针,而不会抛出异常。而gcc的编译器对于C++标准支持比较好,所以当new分配内存失败时会抛出异常。
内存释放:delete
1.和new对应
2.释放后指针指向原来的地址,但无效
3,delete对象指针,会调用对象的析构函数
内存泄漏:指针赋值,指针离开作用域,异常终止
Description
根据头文件Vector.hpp
实现N维Vector
类
//main.cpp
#include <iostream>
#include <cstring>
#include "Vector.h"
#define MAX 51
using namespace std;
int main() {
string name = "";//定义、初始化
int dim, num[MAX];
cin >> name >> dim;
for (int i = 0; i < dim; i++) {
cin >> num[i];
}
Vector vec1(name, dim, num);
vec1.Print();
Vector vec2(vec1);
vec2.Print();
vec1.isEqual(vec2);
int vec3_num[] = {1, 2, 3};
Vector vec3(name, 3, vec3_num);
vec1.isEqual(vec3);
vec2.setName("helloWorld!");
vec1.isEqual(vec2);
return 0;
}
//Vector.h
#ifndef VECTOR_H
#define VECTOR_H
#include <string>
#include <iostream>
using namespace std;
class Vector {
public:
Vector() {
name = "";
dimension = 0;
param = NULL;
}
Vector(string, int, int[]);
Vector(const Vector &otherVec)
{
name = otherVec.name;
dimension = otherVec.dimension;
param = new int[dimension+1];
for (int i = 0; i < dimension; i++)
param[i] = otherVec.param[i];
cout << "copy a vector called " << name << ".\n";
}
~Vector();
void isEqual(const Vector &);
void setName(string);
void Print();
private:
string name;
int dimension, *param;
};
#endif
//Vector.cpp
#include "Vector.h"
using namespace std;
Vector::Vector(string n, int a, int b[])
{
name = n;
dimension = a;
param = new int[a+1];
//开辟空间,大小为a+1
for (int i = 0; i < a; i++)
param[i] = b[i];
//学习如何正确初始化 一个整形数组
cout << "construct a vector called " << name << ".\n";
}
Vector::~Vector()
{
delete [] param;
cout << "release memory from a vector called " << name << ".\n";
}
void Vector::isEqual(const Vector &otherVec)
{
if (name == otherVec.name) {
cout << "same name, ";
} else {
cout << "different name, ";
}
//C++支持直接比较
if (dimension != otherVec.dimension) {
cout << "different value.\n";
return;
}
for (int i = 0; i < dimension; i++) {
if (param[i] != otherVec.param[i]) {
cout << "different value.\n";
return;
}
}
cout << "same value.\n";
}
void Vector::setName(string newName)
{
name = newName;
}
void Vector::Print()
{
cout << name << "(";
for (int i = 0; i < dimension-1; i++)
cout << param[i] << ", ";
cout << param[dimension-1] << ")\n";
//注意输出格式
}
4-H2
Description
拓展完善类Int
,可以加入你觉得需要的成员变量或成员函数。
Sample Input
Sample Output
num 1 is odd? 1
num 2 is odd? 0
1 objects of Int has been constructed.
2 objects of Int has been constructed.
3 objects of Int has been constructed.
2 objects of Int has been constructed.
//通过析构函数释放的顺序确定num的变化方式
//main.cpp
#include <iostream>
using namespace std;
#include"Int.cpp"
int Int::num=0;//静态成员的类外初始化
void f()
{
int i1=1, i2=2;
cout << "num " << i1 << " is odd? " << Int::isodd(1) << endl;
cout << "num " << i2 << " is odd? " << Int::isodd(2) << endl;
Int ii1;
{ Int ii1(i1); Int ii2; }
//出了代码块就销毁,调用析构函数
Int ii2(i2);
//在创建该类型变量时调用构造函数
}
int main()
{
f();
return 0;
}
include <iostream>
using namespace std;
class Int
{
int data;
static int num;
public:
Int()
{
num++;
cout << num << " objects of Int has been constructed.\n";
}
Int(int n)
{
data=n;
num++;
cout << num << " objects of Int has been constructed.\n";
}
static int isodd(int n)
{
if(n%2==0)
return 0;
else
return 1;
}//静态成员变量由静态成员函数处理
~Int()
{
num--;
}
};
4-H3(一直没有搞明白
链表的简单类实现:
#include<iostream>
using namespace std;
typedef int UserData;
//链表结点定义、基本操作、常用操作
#include<iostream>
using namespace std;
typedef int UserData;
class LinkNode{
public:
LinkNode()=default;
LinkNode(UserData item):item(item){};
UserData getData() const{
return item;
}
LinkNode* getNext() const{
return next;
}
void setNext(LinkNode *next){
this->next=next;
}
LinkNode* insert(UserData item);
LinkNode* erase();
static LinkNode* Push(LinkNode *head,UserData item);
static LinkNode* Pop(LinkNode *head);
private:
UserData item;
LinkNode* next;
};
LinkNode* LinkNode::Push(LinkNode *head,UserData item) {//头插法
auto new_head=new LinkNode(item);
new_head->setNext(head);
return new_head;//回到当前位置
}
LinkNode* LinkNode::Pop(LinkNode *head) {//头删法
if(head){
auto new_head=head->getNext();
delete head;
return new_head;//回到当前位置
}
return nullptr;
}
LinkNode*LinkNode::insert(UserData item){//节点后插入
this->next=Push(this->next,item);
return this->next;
}
LinkNode*LinkNode::erase(){//节点后删除
this->next=Pop(this->next) ;
return this->next;
}
int main(){
LinkNode *head=NULL;
head=head->Push(head,3);
head=LinkNode::Push(head,7);
head=LinkNode::Push(head,25);
head=LinkNode::Push(head,4);
head=LinkNode::Pop(head);
head=LinkNode::Pop(head);
auto temp=head;
while(temp!=NULL){
cout<<temp->getData()<<endl;
temp=temp->getNext();
}
return 0;
}
根据Stack.h
完成一个栈类
Sample Input
6
0 1
0 2
1
1
1
1
Sample Outpu
push 1
push 2
pop 2
pop 1
stack is empty!
stack is empty!
s1 is empty: True
Hint
栈是一种后进先出的数据结构。
//main.cpp
#include <iostream>
#include "Stack.h"
using namespace std;
int main() {
Stack s1;
int n, opt, num;
cin >> n;
while (n--)
{
cin >> opt;
if (opt == 0)
{
// push
int num;
cin >> num;
s1.push(num);
cout << "push " << num << endl;
}
else if (opt == 1)
{
// pop
if (!s1.empty())
{
int num = s1.top();
s1.pop();
cout << "pop " << num << endl;
}
else
cout << "stack is empty!" << endl;
}
}
s1.clear();
cout << "s1 is empty: " << (s1.empty() ? "True" : "False") << endl;
return 0;
}
//Stack.h
#ifndef STACK_HPP
#define STACK_HPP
#include <iostream>
class Stack {
private:
struct node
{
int num;
node* next;
node() { num = 0; next = NULL; }
node(int n, node* p = NULL) { num = n; next = p; }
};
node* data;
public:
Stack(); // 构造一个空栈
~Stack(); // 注意内存回收
void push(int); // 入栈
void pop(); // 出栈
int top() const; // 查看栈顶元素,若栈为空,返回0
bool empty() const; // 判断栈是否为空
void clear(); // 清空栈
};
#endif
//stack.cpp
#include "Stack.h"
Stack::Stack()
{
data = NULL;
}
Stack::~Stack()
{
clear();
data = NULL;
}
void Stack::push(int num)
{
node* newNode = new node(num, data);
data = newNode;
}
void Stack::pop()
{
if (this->data != NULL)
{
node* temp = data;
data = data->next;
delete temp;
}
}
int Stack::top() const
{
if (!empty()) return data->num;
else return 0;
}
bool Stack::empty() const
{
return data == NULL;
}
void Stack::clear()
{
while (data != NULL)
{
node* temp = data;
data = data->next;
delete temp;
}
data = NULL;
}
//链表,永恒的痛
需要耐心地画图理解,慢慢想还是可以明白的。
5-K1
与C不同的是,C++使用new和delete去分配和销毁内存。现在你需要应用以上两种方式来学习如何动态管理内存。
//main.cpp
#include<iostream>
#include<string>
#include<iomanip>
#include "allocate.h"
using namespace std;
int main(){
int n;
cin >> n;
int* arr = allocate(n);//开辟空间
for(int i = 0; i < n; i++){
cout << arr[i] << endl;
}
del(arr);//释放空间
return 0;
}
//allocate.h
#include<iostream>
using namespace std;
int* allocate(int n);
void del(int*& arr);
//allocate.cpp
#include "allocate.h"
//一个返回值为指针的函数
int* allocate(int n){
int* arr = new int[n];
for(int i = 0; i < n; i++){
arr[i] = i + 1;
}
return arr;
}
//形参是一个int*类型值的引用
void del(int*& arr){
delete [] arr;
arr = NULL;
//避免出现野指针,将指针赋为NULL
}
5-K2
与 C 不同,C++使用“new”和“delete”来分配和销毁内存。现在您需要实现以下函数来学习如何操作二维数组的动态分配。
int** allocate(int n);
void del(int**& head);
对于每组测试,数字n是需要给出的,你需要输出数字0到(n−1)2。
//main.cpp
#include<iostream>
#include<string>
#include<iomanip>
#include "allocate.h"
using namespace std;
int main(){
int n;
cin >> n;
int** arr = allocate(n);
//二维数组
for(int i = 0; i < n; i++){
for(int j = 0;j < n;j++){
if(j>0){
cout<<" ";
}
cout<<arr[i][j];
}
cout<<endl;
}
del(arr,n);
return 0;
}
//allocate.h
#include<iostream>
using namespace std;
int** allocate(int n);
void del(int**& arr,int n);
//allocate.cpp
#include "allocate.h"
int** allocate(int n){
int sum=0;
int** arr = new int*[n];
//为二维数组整体开辟空间n个行数组的空间
for(int i = 0; i < n; i++){
arr[i]=new int[n];
//为每一个行数组开辟空间
for(int j=0;j<n;j++){
arr[i][j]=sum++;
}
//边开空间边赋值
}
return arr;
//返回一个int**类型值
}
void del(int**& arr,int n){
for(int i=0;i<n;i++){
delete[] arr[i];
}
//记住格式
delete [] arr;
}
5-H1
Create Array
使用C++中的关键字new
和delete
创建和回收数组。
实现以下四个函数:
int* Array(int len);
int** Reshape(int* vec, int row, int col);
void FreeArray(int* vec);
void FreeMatrix(int** mat, int row);
其中,Array
函数创建一个长度为len
的一维数组;Reshape
函数将上述数组变为一个row
×col
的矩阵,题目数据保证row
×col
=len
。
已禁用stdlib.h
,请勿使用malloc
及free
。
//main.cpp
#include "check.h"
// #include <iostream>
#include <stdio.h>
#include "functions.h"
int main() {
int* vec = nullptr;
int len, row, col;
scanf("%d", &len);
vec = Array(len);
for (int i = 0; i < len; ++i) {
scanf("%d", vec + i);
}
int** mat = nullptr;
scanf("%d%d", &row, &col);
mat = Reshape(vec, row, col);
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
printf("%d%c", mat[i][j], j == col - 1 ? '\n' : ' ');
}
}
FreeArray(vec);
FreeMatrix(mat, row);
return 0;
}
//functions.h
#ifndef FUNCTION_H
#define FUNCTION_H
int* Array(int);
int** Reshape(int*, int, int);
void FreeArray(int*);
void FreeMatrix(int**, int);
#endif
//functions.cpp
#include "functions.h"
int* Array(int len) {
int* vec = new int[len];
return vec;
}
int** Reshape(int* vec, int row, int col) {
int** mat = new int*[row];
for (int i = 0; i < row; ++i) {
mat[i] = new int[col];
for (int j = 0; j < col; ++j) {
mat[i][j] = vec[i * col + j];
//十分易错!!!注意是i * col + j
}
}
return mat;
}
void FreeArray(int* vec) {
delete[] vec;
}
void FreeMatrix(int** mat, int row) {
for (int i = 0; i < row; ++i) {
delete[] mat[i];
}
delete[] mat;
}
//记住二维和一维数组的开辟和释放
5-H2
Description
创建数组后,其大小是固定的。有时,您需要向数组添加更多值,但数组已满。在这种情况下,您可以创建一个新的更大的数组来替换现有数组。编写以下函数:
int * tribleCapacity(int *list, int size)
该函数创建一个新数组,使参数列表的大小加倍,并将列表中的值复制到新数组中,剩余值应为零。
int * tribleCapacity(int *list, int size) {
int *list1 = new int[size * 3];
//开辟初始化,节约代码,清晰明了
for(int i = 0; i < 3 * size; i++) {
if(i < size)
*(list1+i) = *(list + i);
else
*(list1+i) = 0;
}
return list1;
}
5-H3(链表相关)
Description
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的各个值。
示例:
输入:
5
1 2 3 4 5
输出:
5 4 3 2 1 //输出的每个数字后都有一个空格,但本题不需要考虑输入与输出格式,因为你只需要写reverselist函数即可
Hint
- 只需写函数reverseList即可,无需考虑题目的输入与输出格式。
- 考察单链表的反转操作,无需考虑内存的泄漏问题(不需要delete)。
//main.cpp
#include<iostream>
#include"ListNode.h"
using namespace std;
extern ListNode* reverseList(ListNode* head);
//在多文件程序中,可以在一个文件(且只能在一个文件)中
//定义一个外部变量。使用该变量的其他文件必须用关键词extern声明他
int main(){
int n;
cin>>n;
ListNode* head=new ListNode(0);
ListNode* cur=head;
while(n>0){
int temp;
cin>>temp;
ListNode* node=new ListNode(temp);
cur->next=node;
cur=cur->next;
n--;
}
ListNode* res=reverseList(head->next);
while(res!=nullptr){
cout<<res->val<<" ";
res=res->next;
}
return 0;
}
//listNode.h
#ifndef LISTNODE_H
#define LISTNODE_H
#include<iostream>
class ListNode{
public:
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
//初始化
};
#endif
//solution.cpp
#include"ListNode.h"
//迭代法
ListNode* reverseList(ListNode* head) {
ListNode* temp; // 保存cur的下一个节点
ListNode* cur = head;
ListNode* pre = NULL;
while(cur) {
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre;
}
//递归法 仅供参考
/*
ListNode* reverseList(ListNode* head) {
// 边缘条件判断
if(head == NULL) return NULL;
if (head->next == NULL) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode *last = reverseList(head->next);
// 翻转头节点与第二个节点的指向
head->next->next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head->next = NULL;
return last;
}
*/
光看是看不懂的,自己画图理解
6-K1
描述
请在my_class.cpp
中实现my_class.h
中的构造函数my_class
my_class(const int a, int &b);
其中,another_a
用于初始化类成员a
,another_b
用于初始化类成员b
参考答案
#include <iostream>
class my_class
{
private:
const int a;
int & b;
public:
my_class(const int another_a, int &another_b);
void print() {std::cout << a << " " << b << std::endl;}
};
//下面这行代码为需要实现的部分
my_class::my_class(const int another_a, int &another_b):a(another_a),b(another_b){};
//构造函数的初始化列表
/*必须使用初始化器列表的时候:
1。常量成员 const成员
2.引用类型:引用必须在定义的时候初始化
3.没有默认构造的类类型,初始化器列表不必调用无参构造函数
注意:成员按照在类中的声明顺序初始化
int main()
{
int a = 1, b = 2;
my_class mc = my_class(a, b);
mc.print();
}
6-K2
描述
请在array.cpp
中实现array.h
中声明的成员函数
// 分配可以容纳size个元素的内存,每个元素赋值为val
array(int size, int val);
array(const array &another);
~array();
// 如果pos不越界,则将pos位置的元素赋值为val,返回true;如果pos越界,则返回false
bool assign(int pos, int val);
//main.cpp
#include <iostream>
#include "array.h"
int main()
{
int n, m;
std::cin >> n;
array arr = array(n, -1);//构造函数要干活
arr.print();
for (int i = 0; i < n; ++i)
{
std::cin >> m;
arr.assign(i, m);
}
arr.print();
array copy_arr = array(arr);//拷贝
copy_arr.print();
for (int i = 0; i < 2 * n; ++i) {
std::cout << copy_arr.assign(i, i) << std::endl;
}
copy_arr.print();
arr.print();
}
//array.h
#ifndef ARRAY
#define ARRAY
#include <iostream>
class array
{
private:
int size; // size
int *data; // dynamically allocate/release memory
public:
array(int size, int val);
array(const array &another);
~array();
bool assign(int pos, int val);
void print() const
{
for (int i = 0; i < size; ++i)
std::cout << data[i] << std::endl;
}
};
#endif
//array.cpp
#include "array.h"
array::array(int size, int val)
{
this->size = size;
this->data = new int[size];
for (int i = 0; i < size; ++i)
{
this->data[i] = val;
}
}
array::array(const array &another)
{
this->size = another.size;
this->data = new int[this->size];
for (int i = 0; i < size; ++i)
{
this->data[i] = another.data[i];
}
}//没有涉及内存的变化,浅拷贝
/*按引用传递,避免在函数调用的过程中生成形参副本
形参声明为const
含指针的类应该重写:构造函数,=重写,析构函数
*/
array::~array()
{
if (data)
delete[] data;
}//为避免内存泄漏,删掉之前先判断在这块内存中是否有东西
bool array::assign(int pos, int val)
{
if (pos >= 0 && pos < size)
{
data[pos] = val;
return true;
}
else
{
return false;
}
}
6-H1
补充:
NAME::NAME(NAME& other) {
if(other.str==NULL){
str=NULL;
return;
}
if(str!=NULL)
delete []str ;
str=new char[strlen(other.str)+1];
if(str!=NULL)
strcpy(str,other.str);
}
NAME& operator=(const NAME& other){
if(other.str==NULL){
str=NULL;
return *this;
}
if(str!=NULL)
delete []str ;
str=new char[strlen(other.str)+1];
if(str!=NULL)
strcpy(str,other.str);//不是str= other.str!!!
return *this;
}
传值的本质是:形参是实参的一份复制
传值传引用实例
void swap_pass_by_reference(int &a,int &b)
{
int t=a;
a=b;
b=t;
}
void swap_pass_by_value(int *a,int *b){
int t=*a;
*a=*b;
*b=t;
}
int main(){
int a=1,b=2;
swap_pass_by_reference(a,b);//传自己
swap_pass_by_value(&a,&b);//传地址
}
描述
浅拷贝 (shallow copy) 只是对指针的拷贝,拷贝后的两个指针的值是相同的,即指向同一个内存空间。深拷贝 (deep copy) 不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经过深拷贝后的指针是指向两个不同地址的指针。
在对象的实例之间相互赋值或定义时,在没有提供给赋值运算符重载(后面的内容)或复制构造函数时,C++编译器会简单地使用浅拷贝的方式来赋值,即两个实例存储的内容完全相同。如果对象中包含了指针成员变量,而在实例中指针成员变量指向了一块动态分配的内存,那么这样的做法是危险的。因为浅拷贝不会新开辟一个空间,然后将指针指向的内容复制进去,两个实例中的成员变量实际上指向了同一块区域。为了解决这种情况,我们需要显示地提供进行深拷贝的成员函数deep_copy
,来进行实例的深拷贝。
在课堂2中,我们实际上是以深拷贝的方式来实现复制构造函数array(const array &another)
,在本题中,我们需要将深拷贝的内容放置到成员函数deep_copy
下,此时的复制构造函数可以简化为
array(const array &another)
{
data = nullptr;//第一步是删掉原来有的值
deep_copy(another);
}
在本题中,我们继续对课堂2的array
进行改造,来完成深拷贝的进阶学习。本题是进阶学习的第一步,在本题中,你需要实现的函数如下
array();
void deep_copy(const array &another);
// 分配可以容纳size个元素的内存,每个元素赋值为val
array(int size, int val);
// 如果pos不越界,则将pos位置的元素赋值为val,返回true;如果pos越界,则返回false
bool assign(int pos, int val);
复制构造函数已经给出,不需要实现,其余函数和课堂2保持一致。
参考答案
//main.cpp
#include <iostream>
#include "array.h"
int main()
{
int n, m;
std::cin >> n;
array arr = array(n, -1);
arr.print();
for (int i = 0; i < n; ++i)
{
std::cin >> m;
arr.assign(i, m);
}
arr.print();
array copy_arr = array(arr);
copy_arr.print();
for (int i = 0; i < 2 * n; ++i) {
std::cout << copy_arr.assign(i, i) << std::endl;
}
copy_arr.print();
arr.print();
copy_arr.deep_copy(array(n / 2, 1));
copy_arr.print();
}
//array.h
#ifndef ARRAY
#define ARRAY
#include <iostream>
class array
{
private:
int size; // size
int *data; // dynamically allocate/release memory
public:
array();
array(int size, int val);
array(const array &another)
{
data = nullptr;
deep_copy(another);
}
~array();
bool assign(int pos, int val);
void deep_copy(const array &another);
void print() const
{
for (int i = 0; i < size; ++i)
std::cout << data[i] << std::endl;
}
};
#endif
//array.cpp
#include "array.h"
#include <cstdlib>//大有用处
array::array()
{
size = 0;
data = nullptr;//c++里最好不用NULL,会有不好的后果
}
array::array(int size, int val)
{
this->size = size;
this->data = new int[size];
for (int i = 0; i < size; ++i)
{
this->data[i] = val;
}
}
array::~array()
{
if (data)
delete[] data;
}
bool array::assign(int pos, int val)
{
if (pos >= 0 && pos < size)
{
data[pos] = val;
return true;
}
else
{
return false;
}
}
void array::deep_copy(const array &another)
{
if (this->data)
{
delete[] this->data;
}//第一步:如果原来有东西,统统删除掉
this->size = another.size;
this->data = new int[this->size];//第二步:建立新的内容,每一个成员都要更新
for (int i = 0; i < this->size; ++i)
{
this->data[i] = another.data[i];
}//第三步:拷贝
}
补充&自学 <cstdlib> (stdlib.h)
此标头定义了几个通用功能,包括动态内存管理,随机数生成,与环境的通信,整数算术,搜索,排序和转换。
abs 整数绝对值(int)
labs 长整数绝对值(long int)
llabs (long long int)
6-H2
描述
在课后1中,我们已经实现了成员函数array::deep_copy
来进行深拷贝。本题在array
的基础上,实现二维数组类array2d
,来继续完成深拷贝的进阶学习。在本题中,你需要实现一个二维数组类array2d
class array2d
{
private:
int row; // row number of 2-D array
int col; // column number of 2-D array
array *data; // dynamically allocate/release memory
public:
// 分配可以容纳row x col个元素的内存,每个元素赋值为val
array2d(int row, int col, int val);
// 析构函数
~array2d();
// 如果(i,j)不越界,则将(i,j)位置的元素赋值为val,返回true;如果(i,j)越界,则返回false
bool assign(int i, int j, int val);
// 深拷贝函数
void deep_copy(const array2d &another);
};
复制构造函数已经给出,不需要实现。
Tips
回忆使用new
和delete
来进行二维数组分配的过程,思考三个问题:
array2d
的内存分配和释放是否和上述过程相同?- 如果不同,则不同点是什么?
deep_copy
的作用体现在哪里?
参考答案
//array.h
#ifndef ARRAY
#define ARRAY
#include <iostream>
class array
{
private:
int size; // size
int *data; // dynamically allocate/release memory
public:
array();
array(int size, int val);
array(const array &another)
{
data = nullptr;
deep_copy(another);
}
~array();
bool assign(int pos, int val);
void deep_copy(const array &another);
void print() const
{
for (int i = 0; i < size; ++i)
std::cout << data[i] << std::endl;
}
};
class array2d
{
private:
int row;
int col;
array *data;
public:
array2d(int row, int col, int val);
array2d(const array2d &another)
{
data = nullptr;
deep_copy(another);
}
~array2d();
bool assign(int i, int j, int val);
void deep_copy(const array2d &another);
void print() const
{
for (int i = 0; i < row; ++i)
{
data[i].print();
}
}
};
#endif
//main.cpp
#include <iostream>
#include "array.h"
int main()
{
int row, col, val;
std::cin >> row >> col;
array2d arr = array2d(row, col, -1);
arr.print();
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < col; ++j)
{
std::cin >> val;
arr.assign(i, j, val);
}
}
arr.print();
array2d copy_arr = array2d(arr);
copy_arr.print();
for (int i = 0; i < 2 * row; ++i)
{
for (int j = 0; j < 2 * col; ++j)
{
std::cout << copy_arr.assign(i, j, i * j) << std::endl;
}
}
copy_arr.print();
arr.print();
copy_arr.deep_copy(array2d(row / 2, col / 2, 1));
copy_arr.print();
}