一只未来程序猿的作业,和MATRIX的斗智斗勇

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) 信息的书本清单,然后产生一个满足以下格式要求的三列输出:

  • namecode 都是 左对齐的
  • cost两位数精度并且是右对齐的,不足两位精度的用0补齐;
  • namecode and cost 的字段宽度分别是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对象,初始化为0
  • point有两个基本属性xy分别记录了x和y轴的坐标,x,y的初始值为0
  • point有一个方法,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的类来表示矩形。该类包含:

  • 两个名为widthheightdouble数据字段,用于指定矩形的宽度和高度。widthheight的默认值均为1。
  • 创建无参数构造函数Rectanglewidthheight默认值均为1。
  • 创建具有指定宽度和高度的Rectangle的构造函数。
  • 所有数据字段的访问器和赋值函数。(getWidth()getHeight()setWidth()setHeight()
  • getArea()函数,返回此“矩形”的区域。
  • getPerimeter()函数,用于返回周长。

与前文有相似,按照要求写即可

3-H2

Description

定义复数类Complex。该类包含:

  • 两个名为realimage的整数变量,分别表示实部和虚部,realimage的默认值均为0。
  • c1.add(c2)表示复数c1c2相加,结果保存在c1之中
  • c1.mul(c2)表示复数c1c2相乘,结果保存在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++中的关键字newdelete创建和回收数组。

实现以下四个函数:

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,请勿使用mallocfree

//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

  1. 只需写函数reverseList即可,无需考虑题目的输入与输出格式。
  2. 考察单链表的反转操作,无需考虑内存的泄漏问题(不需要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用于初始化类成员aanother_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

回忆使用newdelete来进行二维数组分配的过程,思考三个问题:

  • 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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值