内容讲解:
本期培训内容为C++与Java的类与对象的部分,这部分算法方面要求简单,但略显抽象,在各项目开发和实战中,类与对象的使用有极其广泛的应用,是我们重要的工具,这里对类与对象相关知识做简略讲解并结合题目方便大家理解,题目出的不多,但是希望大家都能理解透理解完全!
内容适与zzu仿真招新第三次培训内容适配,也可供C++Java的面向对象的学习。
这里的部分不如算法那么玄妙有趣,较为抽象,但对项目而言确实理解整个架构的重要基础,所以希望大家都能认真阅读讲解和分享的所有链接内容,充实自己丰富学习!
类:把具有相同属性和行为的一类对象抽象为类。类是抽象概念,如人类、犬类等,无法具体到每个实体。
对象:某个类的一个实体,当有了对象后,这些属性便有了属性值,行为也就有了相应的意义。
类是描述某一对象的统称,对象是这个类的一个实例而已。有类之后就能根据这个类来产生具体的对象。一类对象所具备的共同属性和行为(方法)都在类中定义。
class 类名称
{
成员函数:功能
或 成员变量:属性
}
通用的类的定义如上,大家在后续会学习到C语言一个struct的结构体,而
struct定义的类:成员默认的访问权限是public
class定义的类:成员默认的访问权限是private
面向对象有三大特性:
封装: 把相关的数据封装成一个“类”组件
简单来说就是把一个类的所有信息打一个“包”,包含可访问的public和private的选项,实现信息的整合和安全保密。
继承: 是子类自动共享父类属性和方法,这是类之间的一种关系
很明显理解的,子类由父类脱胎而成,父类所有的属性和方法,子类都继承和拥有,父类Student有成员age,子类JuniorStudent也会有age这个属性,通常可以理解为逻辑上的包含关系,外层 父类有的,内层子类也有。
多态: 增强软件的灵活性和重用性
类是有访问限定符的,private,public和protected用以规定访问权限。
类有6个默认函数成员:构造函数、拷贝构造函数、析构函数、赋值运算符重载、取地址操作符重载、const修饰的取地址操作符重载。尤其是构造和析构函数,有重要的属性特色需要掌握了解。
其他还有this指针、static、友元等,学习类与对象这里只做简单提及,或有不周之处,可以参考以下文章:(一定要看一定要看一定要看!!!!)不然会学的很懵
C++类与对象学习讲解:C++类和对象详细总结-CSDN博客
Java类与对象学习详解:Java基础——类和对象_java类和对象的基本概念-CSDN博客
先看他个个把时间!
下面根据题目,讲解涉及的相关知识内容。
如果想直接看类与对象应用的简单实战,直接跳到编程第一题。
一、判断、
1.析构函数是否有返回值???
函数是我们编程语言学习中的要点,各个编程语言这都是十分重要的环节。当然,函数不单单有void操作和一些返回计算的功能函数。C++和java的类与对象,用构造函数和析构函数来初始化和清理对象。阐述如下:
构造函数:主要作用于创建函数时对对象成员的属性赋值。
析构函数:主要作用于在对象销毁前,执行一些清理工作(如释放new开辟在堆区的空间)
项目区别 | 构造函数 | 析构函数 |
语法 | 类名(){} | ~类名(){} |
名称 | 与类名相同 | 与类名相同,名称前加上~ |
参数 | 可以有参数,可重载 | 不可以有参数,不可重载 |
返回值 | 没返回值,不用void | 没返回值,不用void |
调用 | 调用对象时自动构造 | 销毁对象前自动析构 |
不知道重载是什么?简单来说就是同名不同人,冲突如何处理?参考这个函数 重载_函数重载-CSDN博客
两者是特殊的public公有函数,一个类可以有多个构造函数,但只能有一个析构函数。这里用一个C++代码应用简单阐述理解
#include<bits/stdc++.h>
using namespace std;
class op
{
public:
op()//调用对象时自动调用,只能调动一次
{
cout << "这个是构造函数" << endl;
}
~op()//对象销毁前,自动调用,只能调一次
{
cout << "这个是析构函数的调用" << endl;
}
};
void test()//一个简单的创建对象,但是局部变量
{
//创建对象
op ys;
}
int main()
{
op ys1;
cout<<endl;
test();
system("pause");
return 0;
}
左图是调用结果。第一个句话是我main中定义ys1的时候实现的调用,因为直接定义op ys1,所以自动实现了调用,打印了这个句子,(而最后我用pause暂停,所以他没有给ys1打印析构函数,如果删去system("pause")就会最后打印一句析构函数。)
然后我调用了test()函数,这个函数是全局函数,定义了op ys是局部变量,test()跑完了他也就寿命终结了,所以在跑test()的全程中包含了创建和清除,自然实现了构造函数和析构函数的调用了。
另外回归正题,根据定义确实是没有返回值√所以是T
2.关键字struct和class具有相同的功能,struct的默认访问权限是private。???
这里要提到struct和class,他们都具有看似“封装”的特点,(struct在大一下的高级语言程序设计中会学到,但我建议提前学了)struct内容建议自学:很简单【C语言】struct结构体_c语言struct-CSDN博客
很显然的是,相比之下,struct仅仅是一种值类型,用于将一组相关的变量组织为一个单一的变量实体。而class显然复杂的多。简单来说他们的区别
struct | class | |
类型 | 值类型 | 对象类型 |
继承 | 不可以被继承(C++中可以了) | 可以被继承 |
访问权限与默认继承 | 默认是public | 默认是private |
效率 | 效率简单较高 | 有多态和虚拟继承时较低 |
看得出来,C++中有class,struct或许显得有点多余了,这主要是对向下兼容C语言内容的考虑。
本题显然struct默认是public的,class是private的,所以是F错误的,题目的说法是搞反的。
二、选择题
1.在下面有关静态成员函数的描述中,正确的是()。
A.在建立对象前,就可以为静态数据成员赋值
B.静态成员函数在类外定义时要用static前缀
C.静态成员函数只能在类外定义
D.在静态成员函数中可以使用this指针
“静态”在类中的定义很简单,声明函数前加一个“static”即可,是类的一部分,如果类外要调用就要用类名+::引用,在引入静态成员函数之前,C++语言要求所有的成员函数都必须经由该类的对象来调用。而实际上,只有当成员函数中有存取非静态数据成员时才需要类对象。类对象提供this指针给这种函数使用。这个this指针把“在成员函数中存取的非静态数据成员”绑定于“对象内对应的成员”身上。如果没有任何一个成员被直接存取,事实上就不需要this指针,因此也就不需要通过一个类对象来调用一个成员函数。不过C++语言到现在为止还不能识别这种情况。
这样在存取静态数据成员时产生了一些不规则性。如果把静态数据成员声明为nonpublic,就必须同成员函数来存取它。虽然可以不靠类对象来存取static member,但是存取函数必须绑定于类对象上。
而静态成员函数的主要特性是没有this指针。它因此有以下特性:
(1)不能够直接存取类中的nonstatic members。
(2)不能被声明为const、volatile或virtual。
(3)不需要经过类对象才被调用。
A√是C++所规定的
B.由所说和用例可以看出,在类外用类名+::也可以直接调用,只需要类中声明即可。
C类的内外都能使用static成员函数。
D不需要this,这是静态函数的主要特性。
2.class A;
int main(){
A a[10];
A *p[10];
p[0] = new A[10];
}
这道题的设定旨在让大家认识new与delete以及构造函数析构函数的本质和调用情况。
大家这个可以把A类用刚刚判断题第一题的代码定义调用试一下就是构造函数20次,析构函数10次。当然这只是尝试,o(*≧▽≦)ツ下面我给于解析:
首先是定义了A a[10] ,10个A类的元素存贮在容量为10的数组a里面,所以先调用了10次构造函数。
我们构造了个指针组p[10],类的声明定义是分配了实实在在内存的,但是指针只是内存地址,不分配内存,需要使用new进行开辟分配,没有开辟,那么是虚无缥缈的指针,是不需要执行构造函数的,所以A *p[10]这一步不对执行次数有影响。
这里将p[0](别管什么数组下标,其实就是一个A*,一个A类的指针)地址开辟出10个A类的创建,则相当于在这个过程中创建了10个A类,那么调用了10次构造函数。综上调用了20次构造函数,而最后没有system("pause")暂停,到程序终结会清除a[10]调用十次析构函数清除,所以20次构造函数,10次析构函数。
那new出来的10个类为什么不像定义的a[10]的10个A类一样程序完结时执行析构函数清除呢???根本上,对类指针地址new的开辟其实就是执行构造函数,而对应一个delete的删除就是执行析构函数的清除。
#include<iostream>
using namespace std;
class A
{
public:
A()
{ cout << "这个是构造函数" << endl;}
~A()
{cout << "这个是析构函数的调用" << endl;}};
int main()
{
A a[10];
A *p[10];
p[0] = new A[10];
}
执行上述代码出来是打印20次“构造”,10次“析构”。如果我们在程序最后加一个delete[] p[0]就会发现有20次“析构”。到头来是对类指针的操作中,new和delete分别执行构造函数和析构函数的功能。同时注意,这里new了有10个A类大小,你如果执行一个delete p[0]会发现他只打印执行一次“析构”同时程序面临崩溃,更印证了类指针中delete的一次执行,同步进行析构函数一次,这里用delete[] p[0]自动清除完10次。
所以这题选C
三、编程题
1.求圆的面积(类与对象)
直接上代码无需多言,这里用java语言,C++可以参考网上其他内容或者直接对照java翻译过去,这部分无需过多讲解,大家可以多试试多练练。
import java.util.Scanner;
class Circle{
private int radius;//private变量 半径
public Circle() {//无参构造
super();
this.radius=2;//默认2
System.out.println("This is a constructor with no para.");
}
public Circle(int radius) {//和上面那个Circle是重载,靠有参无参进行同名冲突区分
super();
setRadius(radius);
System.out.println("This is a constructor with para.");
}
public int getRadius() {//类成员数值函数,radius是private所以需要类内函数进行访问
return radius;
}
public void setRadius(int radius) {//类成员函数功能,设置半径传参,默认非法输入是2
if(radius<=0)
this.radius=2;
else
this.radius = radius;
}
public double getArea()//类成员函数功能,对类内raduis计算出面积,然后打印
{
return Math.PI*this.radius*this.radius;
}
@Override
public String toString() {//打印的功能函数
return "Circle [radius=" + radius + "]";
}
}
public class Main {
public static void main(String[] args) {
//下面进行定义、打印Circle信息,面积机算结果
Scanner input = new Scanner(System.in);
//1 创建c1对象
Circle c1=new Circle();
System.out.println(c1);
System.out.printf("%.2f\n",c1.getArea());
//2 创建c2对象
Circle c2=new Circle();
System.out.println(c2);
System.out.printf("%.2f\n",c2.getArea());
//3 修改c2对象半径
c2.setRadius(input.nextInt());
System.out.println(c2);
System.out.printf("%.2f\n",c2.getArea());
//4 创建c3对象
Circle c3=new Circle(input.nextInt());
System.out.println(c3);
System.out.printf("%.2f\n",c3.getArea());
input.close();
}
}
2.定义一个Rectangle类
这里用C++表示,可以参考,定义使用都是很简单的
#include<bits/stdc++.h>
using namespace std;
class Rectangle
{
private:
double width;
double height;
public:
Rectangle(double w, double h)
{//之后用Rectangle rect(w,h)设置定义类
width = w;
height = h;
}
void setXY(double a, double b)
{
width=a;
height=b;
}
double getArea() {
return width * height;
}
double getPerimeter() {
return 2 * (width + height);
}
double getWidth() {
return width;
}
double getHeight() {
return height;
}
};
int main()
{
double l,w;
cin>>l>>w;
Rectangle rect(1,1);//默认定义,后面用setXY更改
rect.setXY(l,w);
cout<<"面积为"<<fixed<<setprecision(1)<<rect.getArea()<<endl;
cout<<"周长为"<<fixed<<setprecision(1)<<rect.getPerimeter();
}
3.复数类的操作
呃呃.......其实C++是自带有复数类封装的(手动滑稽)
complex<typedef> ObjectName(realPart, imagePart);
当然这里要你手敲造出来复数类及操作,同时注意友元函数的概念,友元函数不属于类的成员函数
#include<iostream>
using namespace std;
class Complex{
double real,image;
public:
Complex()
{
real=0;
image=0;
}
void get(double r,double i)//获取值,跟上一道题一样
{
real=r;
image=i;
}
void fan()
{
real=-real;
image=-image;
}
void Print()//直接打印
{
cout<<"("<<real<<", "<<image<<")"<<endl;
}
friend void jiao(Complex ob1,Complex ob2)//友元运算,ob1和ob2都同类元,相互之间调用求值
{
cout<<"("<<ob1.real+ob2.real<<", "<<ob1.image+ob2.image<<")"<<endl;
}
};
int main(void)
{
double c11,c12,c21,c22;
cin>>c11>>c12>>c21>>c22;
Complex ob1,ob2;
ob1.get(c11,c12);
ob2.get(c21,c22);
jiao(ob1,ob2);
ob2.fan();
jiao(ob1,ob2);
ob2.fan();
ob2.Print();
}
4.最长连续不重复序列
时间复杂度的概念大家会在数据结构里面学到,在空间复杂度相同的情况下,时间复杂度越低代码的执行效率越高。
这道题用暴力算法的时间复杂度为O(N^2),而改进的双指针算法时间复杂度为O(N)
双指针算法通常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作常有以下应用场景
对于这道题,可以拆分成几个子问题:
如何判断一个区间内是否有重复数字?
用一个int数组,数组的index(也就是数组a[ ]里面的数字)就是题目给出的n个数字,数组的value(也就是数组a[index]的值)就是n个数字的出现次数。
如何维护一个区间?
可以保存该区间的start和end的index。我们可以把每个数字都当做end,来看它前面的区间是否符合条件,这样就可以平稳地遍历一轮。
确定end之后,start的值可以向后移动,直到start和end的区间不含重复数字为止。
如何确定最大区间?
每次得到一个区间,都计算该区间的长度,并将这个长度与已经得到的最大值进行比较,如果比最大值大,就更新最大值。这部分大家肯定都会。
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100010;
int n;
int a[maxn], s[maxn]; //a用来存输入的数 s用来存a中数出现的个数
int main(){
int ans = 0;
cin >> n;
for(int i = 0; i < n; i ++ ) cin >> a[i];
for(int i = 0, j = 0; i < n; i ++ ){
s[a[i]]++; //i指针右移,a[i]出现的次数+1
while(s[a[i]] > 1){ //a[i]出现的次数大于1,说明出现了重复元素
s[a[j]]--; //a[j]出现的次数-1
j++; //j指针右移
}
ans = max(ans, i - j + 1);
}
cout << ans << endl;
return 0;
}
当然这里也可以用stl的map把对应符号与值绑定,不用再设置s作为a数组的存储,这个有兴趣同学可以回头试试。
5.数组循环左移
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m,temp;
cin>>n>>m;
m=m%n;//移动超过总长度n的时候,重复一次就回到原来,所以m=m%n令他等于有效移动量<n
int a[n];//存储总数组
for(int i=0;i<n;i++)
cin>>a[i];
while(m--){
temp=a[0];
for(int i=0;i<n-1;i++)
a[i]=a[i+1];
a[n-1]=temp;
}//进行存储首元素+所有元素左移+末尾元素存是首元素
for(int i=0;i<n;i++){
if(i!=0)
cout<<' ';
cout<<a[i];
}
}
算法编程题部分十分简单,这里不多做赘述,对于类的内容需要多操作才能加深深刻理解!!请务必学会类的理论!!
编写成员:ZZU2D-Lihao,ZZU2D-Li Xinyi
原创编写,内容引用链接已标注,若有侵权或编写内容有误情况,可私信联系,感谢您的指正。