实验目的
掌握拷贝构造函数和赋值运算符的使用方法
掌握静态成员变量和静态成员函数的使用方法
了解C++友元机制和掌握友元的使用方法
实验内容
(重点掌握)给出学生类的声明如下:
class Student
{
public:
enum Gender { Male, Female }; //枚举类型,性别
//声明友元函数,用于将枚举类型输出文本形式
friend std::ostream& operator<<(std::ostream& os, Gender gender);
Student(); //无参数构造函数
Student(const char* name, Gender gender); //带参数构造函数
Student(const Student& student); //拷贝构造函数
Student& operator= (const Student& student); //赋值运算符
~Student(); //析构函数,释放内存
void Show() const; //常成员函数用于显示学生姓名和性别
private:
char* name_; //名字
Gender gender_; //性别
};
完成学生类的实现,并利用下面给出的main函数测试所有的成员函数。(可以尝试在常成员函数中修改成员数据,观察编译器是否报错)。
int main()
{
Student s1;
Student s2("Nicolas ZhaoSi", Student::Gender::Male);
Student s3("Jason GuoDa Statham", Student::Gender::Male);
Student s4(s2);
s1 = s3;
s1.Show();
s2.Show();
s3.Show();
s4.Show();
}
声明一个Student类,在该类中包含一个数据成员表示学生成绩、两个静态数据成员表示总成绩和学生人数;有参构造函数用于设置成绩、累计成绩之和、累计学生人数,一个静态函数用于返回学生的成绩之和,另一个静态成员函数用于取全部成绩的平均分。在main函数中输入某班同学的成绩,并调用上述函数求出全部学生的成绩之和和平均分。
class Student
{
public:
Student(){}
Student(double score); //设置成绩
static double Sum(); //返回成绩总和
static double Average(); //求平均成绩
private:
int score_; //成绩
static double total_score_; //总成绩
static double total_numbers_; //学生人数
}
int main()
{
int n; //学生总数
cin >> n; //键盘输入学生总数
//根据输入的学生人数创建学生数组
//可以从键盘输入学生成绩,也可以随机生成。
return 0;
}
(重点掌握)定义一个平面点的类Point,其中x和y坐标设为私有数据成员。定义线段类Line,数据成员包括线段的两个端点、线段的长度,定义线段类Line的常成员函数用于显示线段端点坐标和线段长度。完成两个类的构造函数和一些必要的成员函数,并在main函数中构建两条线段,调用常成员函数显示结果。
class Point
{
public:
Point(); //无参构造函数
Point(int x, int y); //有参构造函数
int get_x() const; //返回x坐标
int get_y() const; //返回y坐标
double Distance(const Point& point);//计算到当前点到点point的距离
//重载输出运算符,格式化输出点(x,y)
friend ostream& operator<<(ostream& os, const Point& point);
private:
int x_;
int y_;
};
class Line
{
public:
Line();//无参构造函数
Line(int x1, int y1, int x2, int y2); //(x1,y1)和(x2,y2)两个点构造线段
Line(Point& p1, Point& p2);//p1和p2两个点构造线段
double Length() const; //返回线段长度
void Show() const; //显示线段的两个端点以及线段长度
private:
Point start_point_;
Point end_point_;
double length_;
};
int main()
{
Point p1(1, 1);
Point p2(2, 2);
Line line1(p1, p2);
Line line2(1, 2, 3, 4);
line1.Show();
line2.Show();
}
代码
作业1
第一个在vs里会报错,析构函数那里注释掉就能运行了,clion就没问题
dev里删掉:Gender:
就行
#define _CRT_SECURE_NO_DEPRECATE
#include<string.h>
#include<iostream>
using namespace std;
class Student {
public:
enum Gender { Male, Female }; //枚举类型,性别
//声明友元函数,用于将枚举类型输出文本形式
friend std::ostream& operator<<(std::ostream& os, Gender gender);
Student();//无参数构造函数
Student(const char* name, Gender gender);//带参数构造函数
Student(const Student& student); //拷贝构造函数
Student& operator= (const Student& student); //赋值运算符
~Student(); //析构函数,释放内存
void Show() const;//常成员函数用于显示学生姓名和性别
private:
char* name_; //名字
Gender gender_; //性别
};
std::ostream& operator<<(std::ostream& os, Student::Gender gender)
{
if (gender == 0) {
return os << "Male" << endl;
}
else {
return os << "Female" << endl;
}
}
Student::Student()
{
this->name_ = NULL;
this->gender_ = Male;
}
Student::Student(const char* name, Gender gender)//带参数构造函数
{
this->name_ = new char[strlen(name) + 10];
strcpy(name_, name);
this->gender_ = gender;
}
Student::Student(const Student& student) //拷贝构造函数
{
this->name_ = new char[strlen(student.name_) + 10];
strcpy(this->name_, student.name_);
this->gender_ = student.gender_;
}
Student& Student::operator = (const Student& student) //赋值运算符
{
this->name_ = student.name_;
this->gender_ = student.gender_;
return *this;
}
Student::~Student() //析构函数,释放内存
{
delete[] name_;
delete[] & gender_;
}
void Student::Show() const//常成员函数用于显示学生姓名和性别
{
cout << this->name_ << endl;
cout << this->gender_ << endl;
}
int main()
{
Student s1;
Student s2("Nicolas ZhaoSi", Student::Gender::Male);
Student s3("Jason GuoDa Statham", Student::Gender::Male);
Student s4(s2);
s1 = s3;
s1.Show();
s2.Show();
s3.Show();
s4.Show();
return 0;
}
作业1改进
这串代码在clion和vs里面都不会报错
问题出现在析构函数和操作符重载那里,析构函数没有判断原来就是NULL的情况,操作符重载那里是忘记用new动态开辟空间了…(太粗心了~_~
)
不得不说,vs的严格程度还是比clion高很多的,但是clion的警告倒是一堆
如果dev要运行,还是要删掉:Gender:
#define _CRT_SECURE_NO_DEPRECATE
#include<string.h>
#include<iostream>
using namespace std;
class Student {
public:
enum Gender { Male, Female }; //枚举类型,性别
//声明友元函数,用于将枚举类型输出文本形式
friend std::ostream& operator<<(std::ostream& os, Gender gender);
Student();//无参数构造函数
Student(const char* name, Gender gender);//带参数构造函数
Student(const Student& student); //拷贝构造函数
Student& operator= (const Student& student); //赋值运算符
~Student(); //析构函数,释放内存
void Show() const;//常成员函数用于显示学生姓名和性别
private:
char* name_; //名字
Gender gender_; //性别
};
std::ostream& operator<<(std::ostream& os, Student::Gender gender)
{
if (gender == 0) {
return os << "Male" << endl;
}
else {
return os << "Female" << endl;
}
}
Student::Student()
{
this->name_ = NULL;
this->gender_ = Male;
}
Student::Student(const char* name, Gender gender)//带参数构造函数
{
this->name_ = new char[strlen(name) + 10];
strcpy(this->name_, name);
this->gender_ = gender;
}
Student::Student(const Student& student) //拷贝构造函数
{
this->name_ = new char[strlen(student.name_) + 10];
strcpy(this->name_, student.name_);
this->gender_ = student.gender_;
}
Student& Student::operator = (const Student& student) //赋值运算符
{
// if (this == &student)//判断是不是它本身,是的话返回this
// return *this;
// if (this->name_ != NULL){//vs有没有这仨都不会报错,但是加上比较好
// delete this->name_;
// }
// if (this->gender_ != NULL){
// delete &this->gender_;
// }
this->name_ = new char [strlen(student.name_) + 10];//记住要开辟空间
strcpy(this->name_,student.name_);
this->gender_ = student.gender_;
return *this;
}
Student::~Student() //析构函数,释放内存
{
if (name_!=NULL){//需要考虑空间本来就是null的情况,不然vs会出现异常
delete[] name_;
name_ = NULL;
}
if (gender_ != NULL){
delete[] & gender_;
gender_ = static_cast<Gender>(NULL);
}
}
void Student::Show() const//常成员函数用于显示学生姓名和性别
{
cout << this->name_ << endl;
cout << this->gender_ << endl;
}
int main()
{
Student s1;
Student s2("Nicolas ZhaoSi", Student::Gender::Male);
Student s3("Jason GuoDa Statham", Student::Gender::Male);
Student s4(s2);
s1 = s3;
s1.Show();
s2.Show();
s3.Show();
s4.Show();
}
作业2
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
using namespace std;
class Student {
public:
Student() {}
Student(double score);
Student(int score);
static double Sum(); //返回成绩总和
static double Average(); //求平均成绩
private:
int score_; //成绩
static double total_score_; //总成绩
static double total_numbers_; //学生人数
};
double Student::total_score_ = 0;
double Student::total_numbers_ = 0;
Student::Student(double score) {
this->score_ = score;
total_score_ += score;
total_numbers_++;
}
Student::Student(int score) {
this->score_ = score;
total_score_ += score;
total_numbers_++;
}
double Student::Sum() //返回成绩总和
{
return total_score_;
}
double Student::Average() //求平均成绩
{
return total_score_ / total_numbers_;
}
Student* student[1000000];//创建对象数组
int main() {
int n; //学生总数
cin >> n; //键盘输入学生总数
for (int i = 0; i < n; ++i) {
double a = rand() % 101;
student[i] = new Student(a);
}
cout << Student::Average() << endl;
cout << Student::Sum() << endl;
return 0;
}
作业3
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include <math.h>
using namespace std;
class Point {
public:
Point(); //无参构造函数
Point(int x, int y); //有参构造函数
int get_x() const; //返回x坐标
int get_y() const; //返回y坐标
double Distance(const Point& point);//计算到当前点到点point的距离
//重载输出运算符,格式化输出点(x,y)
friend ostream& operator<<(ostream& os, const Point& point);
private:
int x_;
int y_;
};
Point::Point() //无参构造函数
{
x_ = 0;
y_ = 0;
}
Point::Point(int x, int y) //有参构造函数
{
x_ = x;
y_ = y;
}
int Point::get_x() const //返回x坐标
{
return x_;
}
int Point::get_y() const //返回y坐标
{
return y_;
}
double Point::Distance(const Point& point) //计算到当前点到点point的距离
{
return sqrt(pow((point.get_x() - this->x_), 2) + pow((point.get_y() - this->y_), 2));
}
//重载输出运算符,格式化输出点(x,y)
ostream& operator<<(ostream& os, const Point& point)
{
return os << "x:" << point.x_ << " " << "y:" << point.y_;
}
class Line {
public:
Line();//无参构造函数
Line(int x1, int y1, int x2, int y2); //(x1,y1)和(x2,y2)两个点构造线段
Line(Point& p1, Point& p2);//p1和p2两个点构造线段
double Length() const; //返回线段长度
void Show() const; //显示线段的两个端点以及线段长度
private:
Point start_point_;
Point end_point_;
double length_;
};
Line::Line()//无参构造函数
{
start_point_ = Point(0, 0);
end_point_ = Point(0, 0);
length_ = Length();
}
Line::Line(Point& p1, Point& p2)//p1和p2两个点构造线段
{
this->start_point_ = p1;
this->end_point_ = p2;
this->length_ = start_point_.Distance(end_point_);
}
Line::Line(int x1, int y1, int x2, int y2) //(x1,y1)和(x2,y2)两个点构造线段
{
Point p1 = Point(x1, y1);
Point p2 = Point(x2, y2);
this->start_point_ = p1;
this->end_point_ = p2;
this->length_ = start_point_.Distance(end_point_);
}
double Line::Length() const //返回线段长度
{
return this->length_;
}
void Line::Show() const //显示线段的两个端点以及线段长度
{
cout << this->start_point_ << endl;
cout << this->end_point_ << endl;
cout << this->length_ << endl;
}
int main() {
Point p1(1, 1);
Point p2(2, 2);
Line line1(p1, p2);
Line line2(1, 2, 3, 4);
line1.Show();
line2.Show();
}
参考:
C++拷贝构造函数详解
一文说尽C++赋值运算符重载函数(operator=)//这位大佬讲的很全
C++静态成员变量与静态成员函数