C++学习之第三天

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_49278191/article/details/121025956