Day3-构造函数和析构函数
1.知识点总结:
共同点:
a.在类中如果没有提供这两个函数,系统默认给这两个函数提供空实现。
b.构造函数和析构函数必须声明在类全局的作用域(public)之下,否则外界是访问不到的
c.构造函数没有返回,也不用写void(1)构造函数---作用:初始化数据成员。
1.函数名与类名相同,可以有参数,可以发生重载
2.实例化对象时,由编译器自动调用一次,不需要手动调用
3.如何使用构造函数对数据成员进行初始化,注意初始化和赋值的区别。
(2)构造函数的分类以及调用。
a.构造函数的分类
1.按照参数分类:有参构造函数、无参构造函数(编译器默认提供)
2.按照类型分类:普通构造函数,拷贝构造拷贝构造函数
b.构造函数的调用方法
1.构造函数的的调用规则。
2.构造函数的调用方法
1.括号法---实例化对象直接加括号,括号里是构造函数要初始化或者进行赋值的参数列表,或为空
2.显式法---实际上是一个拷贝构造。
3.隐式法---实例化对象时,参数直接作为对象的初始化
(3)拷贝构造函数---重要
1.拷贝构造函数的调用3个时机
2.深拷贝与浅拷贝
3.两个问题:
1.拷贝构造函数中的const为什么不能去掉?熟知原理不能去掉,如果去掉,当传递的参数是右值的时候,非const左值引用不能绑定到 右值,所以就会报错或者说非const左值引用不能识别右值.
2.拷贝构造函数的引用符号为什么不能去掉?熟知原理引用符号不能去掉,如果去掉,使用拷贝构造函数的时候,会进行形参与实参的结 合,这个时候会调用拷贝构造函数,会继续满足拷贝构造函数的调用时机,会无限调 用下去,然后我们知道函数的参数会入栈,而栈是有大小的,所以最终回导致栈溢 出,然后程序就会崩溃,所以引用符号不能去掉
(4)析构函数---作用:类的数据成员的清理工作。
1.在函数名面前加~,就是析构函数,在类中只能有一个,不允许发生重载。
2.在类的对象销毁的时候自动调用
3.允许被显式调用,显式调用后,对象的数据会被清空,不建议使用。
一、简答题1.设A为test类的对象且赋有初值,则语句test B(A); 表示。
答:调用test类的拷贝构造函数,把A的内容当成对象B的初始值。
2.利用“对象名.成员变量”形式访问的对象成员仅限于被声明为 (1)的成员;若要访问其他成员变量,需要通过 (2) 函数

(1)public
(2)类封装的成员函数
3、浅拷贝与深拷贝区别?
1.浅拷贝:同一类型的对象之间可以赋值,可以使得两个对象的成员变量的值相同,但两个对象仍然是独立的,这种情况为浅拷贝。当类中有指针,并且指针指向的是动态分配的内存空间,析构函数做了动态内存释放的处理,可能会导致重释放的问题。
2.深拷贝:当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,进行内容拷贝,就是深拷贝。
二、写出下面程序结果。1、写出以下程序运行的结果。( )
#include <iostream>
using std::cout;
using std::endl;
class Sample
{
public:
Sample();
void Display();
private:
int i;
static int k;
};
Sample::Sample()
{
i=0;
k++;
}
void Sample::Display()
{
cout << "i=" << i << ",k=" << k << endl;
}
int Sample::k=0;
int main( )
{
Sample a, b;
a.Display();
b.Display();
return 0;
}
运行结果:
i=0,k=2 //k为2的原因,实例化了两个对象,静态变量k加了两次
i=0,k=2
2、设有如下程序结构:
class Box
{
//....
};int main()
{
Box A,B,C;
}
该程序运行时调用_3_次构造函数;调用 _3_次析构函数。
3、写出下面程序的运行结果()
#include <iostream>
using std::cout;
using std::endl;
class Sample
{
int i;
public:
Sample();
Sample(int val);
void Display();
~Sample();
};
Sample::Sample()
{
cout << "Constructor1" << endl;
i=0;
}
Sample::Sample(int val)
{
cout << "Constructor2" << endl;
i=val;
}
void Sample::Display()
{
cout << "i=" << i << endl;
}
Sample::~Sample()
{
cout << "Destructor" << endl;
}
int main()
{
Sample a, b(10);
a.Display();
b.Display();
return 0;
}
Constructor1
Constructor2
i=0
i=10
Destructor //vs下会看不到,阻塞在system("pause")下,linux下可以看到
Destructor //vs下会看不到
4、设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;
void main()
{
A *pa=new A();
B b;
static D d;
delete pa;
}
答:
构造函数调用顺序:C,A,B,D
析构函数调用顺序:A,B,D,C
/*
A先被释放,碰到了delete pa,
1.main函数栈上的局部对象先被释放,b
2.局部对象先释放,再释放局部静态对象,d
3.全局对象最后释放
*/
自己写的测试代码
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A的构造函数"<<endl;
}
~A()
{
cout<<"A的析构函数"<<endl;
}
};
class B
{
public:
B()
{
cout<<"B的构造函数"<<endl;
}
~B()
{
cout<<"B的析构函数"<<endl;
}
};
class C
{
public:
C()
{
cout<<"C的构造函数"<<endl;
}
~C()
{
cout<<"C的析构函数"<<endl;
}
};
class D
{
public:
D()
{
cout<<"D的构造函数"<<endl;
}
~D()
{
cout<<"D的析构函数"<<endl;
}
};
C c;
int main()
{
A *pa = new A();
B b ;
static D d;
delete pa;
return 0;
}
/*
C的构造函数
A的构造函数
B的构造函数
D的构造函数
A的析构函数
B的析构函数
D的析构函数
C的析构函数
*/
5、写出下面程序的结果:
#include<iostream>
using std::cout;
using std::endl;
int i = 1;
class Test
{
public:
Test()
:_fourth(_third)//3
,_second(i++)//2
,_first(i++)//1
,_third(i++)//3
{
_third = i;//4
}
void print()
{
cout << "result : " << _first + _second + _third + _fourth << endl;//1+2+4+4
}
private:
int _first;
int _second;
int _third;
int &_fourth;//注意:与前面学的引用类比即可
};
int main()
{
Test test;
test.print();
return 0;
}
result : 11
分析:初值:first(1),second(2),third(3),fourth(3)
程序初始化后,i=4,进入构造函数体,third = 4, fourth=4。
Ps:对数据成员的初始化,与其在初始化列表的顺序无关,与数据成员在类中的声明顺序有关。
6、下列代码在编译时会产生错误的是()
#include <iostream>
using std::cout;
using std::endl;
struct Foo
{
Foo()
{
}
Foo(int)
{
}
void fun()
{
}
};
int main(void)
{
Foo a(10);//语句1
a.fun();//语句2
Foo b();//语句3
b.fun();//语句4
return 0;
}
解析:
语句3错误,不能用括号法来调用无参数构造函数,这样写的话编译器会把这句代码当成函数声明来看
Foo b();//b是一个函数,返回的类型是Foo
正确定义方式:Foo b;
三、改错题。例题1:分析找出以下程序中的错误,说明错误原因,给出修改方案使之能正确运行。
#include <iostream>
using std::cout;
using std::endl;
class Base
{
int a1,a2; //错误1: 此处为默认private,不允许外界调用
public:
Base(int x1 = 0, x2 = 0); //错误2: 此处只给了声明,缺少了定义,而且x2未定义类型
};
int main()
{
Base data(2,3);
cout << data.a1 << endl;
cout << data.a2 << endl;
return 0;
}
修正:
#include <iostream>
using std::cout;
using std::endl;
class Base
{
public://<-添加
int a1, a2;
public:
Base(int x1 = 0, int x2 = 0):a1(x1),a2(x2){};//<----修改部分
};
int main()
{
Base data(2, 3);
cout << data.a1 << endl;
cout << data.a2 << endl;
return 0;
}
例题2:分析以下程序的错误原因,给出修改方案使之能正确运行。
#include <iostream>
using std::cout;
using std::endl;
class Base
{
float _ix;
float _iy;
public:
Base(float ix,float iy)
{
_ix = ix;
_iy = iy;
}
float gain();
};
Base::float gain() //错误1: 返回值类型放错位置,作用域限定符要 ::与函数名连在一起。
{
return _iy/_ix;
}
int main()
{
Base base(5.0,10.0);
cout << "The gain is => " << gain() << endl; // 错误2:公有对象成员的正确访问方式: 对象.成员
return 0;
}
修改后:
#include <iostream>
using std::cout;
using std::endl;
class Base
{
float _ix;
float _iy;
public:
Base(float ix,float iy)
{
_ix = ix;
_iy = iy;
}
float gain();
};
float Base::gain()
{
return _iy/_ix;
}
int main()
{
Base base(5.0,10.0);
cout << "The gain is => " << base.gain() << endl;
return 0;
}
四、编程题。1、定义一个学生类,其中有3个数据成员:学号、姓名、年龄,以及若干成员函数。同时编写main函数使用这个类,实现对学生数据的赋值和输出。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
class Student
{
public:
Student(int num,string student_Name,int age);
void print();
private:
int s_Num;//学号
string s_Name;//姓名
int s_Age;//年龄
};
Student::Student(int num, string student_Name, int age)
{
s_Num = num;
s_Name = student_Name;
s_Age = age;
}
void Student::print()
{
cout << "学号:" << s_Num << ",姓名:" << s_Name << ",年龄:" << s_Age << endl;
}
int main()
{
Student s1(36, "张三", 18);//实例化对象
s1.print();
system("pause");
return EXIT_SUCCESS;
}
2、编写一个程序计算两个给定的长方形的周长和面积。
//长方形周长= 2(a+b),面积=a*b
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
class Rectangle
{
public:
Rectangle(int length, int width);
void print();//打印长方形的有关信息
int area();//求长方体的面积
int girth();//周长
private:
int rec_Length;
int rec_Width;
};
Rectangle::Rectangle(int length, int width) :rec_Length(length), rec_Width(width)
{};
void Rectangle::print()
{
cout << "长度=" << rec_Length
<< "\t宽度=" << rec_Width
<< '\n' << "周长=" << girth()
<< "\t面积=" << area()<<endl;
}
int Rectangle::area()
{
return rec_Length* rec_Width;
}
int Rectangle::girth()
{
return 2 * (rec_Length + rec_Width);
}
int main()
{
Rectangle rec1(10, 20);
rec1.print();
system("pause");
return EXIT_SUCCESS;
}
3、编写一个类,实现简单的栈。栈中有以下操作:
> 元素入栈 void push(int); > 元素出栈 void pop(); > 读出栈顶元素 int top(); > 判断栈空 bool emty(); > 判断栈满 bool full(); 如果栈溢出,程序终止。栈的数据成员由存放10个整型数据的数组构成。(可以自己设计入栈出栈的数据)
提示:就是用C++类的方式实现一个栈,然后写出栈的基本操作,入栈、出栈、栈为空、栈为满的函数,以及模拟栈的入栈出栈的操作。
mystack.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
#define MAX 10
class Stack
{
public:
Stack();
void stack_Push(int elment);//压栈
void stack_Pop();//出栈
int stack_Get_top();//返回栈顶元素
bool is_Empty();//栈空为false,栈不空为true
bool is_Full();//栈满为true
void stack_Print();//把栈中元素取出打印
~Stack();
private:
int S_size;//动态数组初始容量,有默认值,可指定
int* S_pArray;//动态数组,初始化的时候,在堆空间申请一个数组,用来维护栈,默认空间10
int S_top;//栈顶元素
};
mystack.cpp
#include "myStack.h"
//1.初始化
Stack::Stack()
{
S_pArray = new int[MAX];//堆空间开辟数组
S_top = -1; //栈顶指针,始终指向栈顶元素
S_size = MAX; //容量大小,可以进行扩展
}
//2.入栈
void Stack::stack_Push(int element)
{
if (S_top - 1 == S_size)
return;
S_pArray[++S_top] = element; //栈顶指针先加1再入栈
}
//3.出栈
void Stack::stack_Pop()
{
if (S_top == -1)//栈空,返回
return;
S_top--;//不需要返回弹出的元素,栈顶指针减1即可
}
//4.获取栈顶元素
int Stack::stack_Get_top()
{
if (S_top == -1)//栈空,返回
return -1;
return S_pArray[S_top];//栈顶指针始终指向栈顶元素
}
//判栈空
bool Stack::is_Empty()
{
if (S_top == -1)
{
return false;//栈空返回false
}
return true;
}
//判栈满
bool Stack::is_Full()
{
if (S_size - 1 == S_top)//栈满返回true
{
return true;
}
return false;
}
//打印栈中元素
void Stack::stack_Print()
{
while (is_Empty())//非空则打印
{
cout<< S_pArray[S_top] << endl;
stack_Pop();//打印完弹出
}
}
//毁栈
Stack::~Stack()
{
if (S_pArray)
{
delete[] S_pArray;
S_pArray = nullptr;
}
}
main.cpp
#include "myStack.h"
void test01()
{
Stack myStack;
myStack.stack_Push(20);
myStack.stack_Push(30);
myStack.stack_Push(40);
myStack.stack_Push(50);
cout << "获取栈顶元素:" << myStack.stack_Get_top() << endl;
myStack.stack_Push(60);
cout << "打印栈中的元素:" << endl;
myStack.stack_Print();
}
int main()
{
test01();
system("pause");
return 0;
}
4、编写一个类,实现简单的队列。队列中有以下操作: > 元素入队 void push(int); > 元素出队 void pop(); > 读取队头元素 int front(); > 读取队尾元素 int back(); > 判断队列是否为空 bool emty(); > 判断队列是否已满 bool full();
注意循环队列的使用
提示:就是用C++类的方式实现一个队列,然后写出队列的基本操作,入队列、出队列、队列为空、队列为满的函数,以及模拟队列的入队列出队列的操作。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
class Queue
{
public:
Queue(int size);//初始化队列
//1.入队
void push(int element);
//2.出队
void pop();
//3.获取队头元素
int front_func();
//4.获取队尾元素
int back();
//判断队释放为空
bool empty();
//判断队是否满
bool full();
~Queue();
private:
int front;
int rear;
int MaxSize;
int *queue_data;//动态
};
Queue::Queue(const int size)
{
//初始化
queue_data = new int[size];
front = 0;
rear = 0;
MaxSize = size;
}
//1.入队
void Queue::push(int element) {
if ((rear + 1) % MaxSize == front)
{
cout << "队已满,禁止插入" << endl;
return;//队满
}
queue_data[rear] = element;
rear = (rear + 1) % MaxSize;
}
//2.出队
void Queue::pop()
{
if (rear == front)
{
cout << "队已空" << endl;
return;
}
front = (front + 1) % MaxSize;//队头指针加1
}
//3.获取队头元素
int Queue::front_func()
{
if (front == rear)//队空
{
cout << "队已空" << endl;
return -1;
}
return queue_data[front];
}
//4.获取队尾元素
int Queue::back()
{
if (front == rear)//队空
{
cout << "队已空" << endl;
return -1;
}
return queue_data[rear-1];
}
//判断队释放为空
bool Queue::empty()
{
return rear == front;
}
bool Queue::full()
//判断队是否满
{
return (rear + 1) % MaxSize == front;
}
Queue::~Queue()
{
if (queue_data)
{
delete[] queue_data;
queue_data = nullptr;
}
}
int main()
{
Queue myqueue(10);
myqueue.push(12);
myqueue.push(13);
myqueue.push(14);
myqueue.push(16);
myqueue.push(18);
myqueue.push(133);
myqueue.push(140);
myqueue.push(150);
myqueue.push(160);
cout << "获取队头元素:" << myqueue.front_func() << endl;
cout << "获取队尾元素:" << myqueue.back() << endl;
myqueue.pop();
cout << "获取队头元素:" << myqueue.front_func() << endl;
cout << "获取队尾元素:" << myqueue.back() << endl;
myqueue.pop();
myqueue.pop();
myqueue.pop();
myqueue.pop();
myqueue.pop();
myqueue.pop();
myqueue.pop();
myqueue.pop();
myqueue.pop();
cout << "获取队头元素:" << myqueue.front_func() << endl;
cout << "获取队尾元素:" << myqueue.back() << endl;
system("pause");
return EXIT_SUCCESS;
}