ZZU仿真第三次培训讲解(类与对象)

内容讲解:

本期培训内容为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显然复杂的多。简单来说他们的区别

structclass
类型值类型对象类型
继承不可以被继承(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

原创编写,内容引用链接已标注,若有侵权或编写内容有误情况,可私信联系,感谢您的指正。

  • 13
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值