编程语言的一些基础概念(三):面向对象

2023-10-27

在前面两篇中,主要讲了函数式编程语言的一些基础概念。这篇是 Coursera Programming Languages, Part C 的总结,通过 Ruby 介绍面向对象编程里的一些概念。了解这些概念能让你在上手任何一门新的面向对象语言时,都更加得心应手。

虽然用的是 Ruby,但是不会涉及很深的 Ruby,即使不懂 Ruby,读下来应该没问题。对于已经了解面向对象编程的朋友,可以考虑直接跳到子类和继承那部分,或许你会有一些新的启发。

面向对象编程 & Ruby

面向对象编程(Object Oriented Programming)简称 OOP,像 Java,C++ 等语言的主要编程模式都是面向对象的。OOP 主要是通过 对象(Object) 来抽象表示,比如说平面上的一个点,可以抽象表示成 Point(x, y),x,y 表示这个点的横纵坐标。在对象中可以有一些方法(methods) 来具体表示这个对象能做些什么,比如对于一个点,可定义一个 method distFromOrigin ,去求这个点的到原点的距离。

Everything is object in Ruby

Ruby 是动态类型的面向对象语言。Ruby 中所有的表达式都是一个对象,比如数字 1, 2, 3, 4 等是对象,+ 加法是对象的一个方法。Ruby 最出名的是在网页应用的开发,但是因为 Everything is object in Ruby,被选做为这个课程的教学语言。

Class & Methods

在 OOP 中,最基本的就是怎么定义和使用对象以及对象中的方法。

定义 Class 和 Methods

Ruby 中,对象的定义通过 Class 实现。

class Foo
  def m1
    ...
  end
  def m2 (x,y)
    ...
  end
end

这里定义了一个对象 Foo,以及两个方法 m1 和 m2。

调用方法

foo = Foo.new
foo.m1
foo.m2(x, y)

上例是最简单的方法调用。

 e0.m(e1, ..., en)

这是调用方法的抽象表达,可以有另一种理解: 发送消息,e0 是消息的接受者,可以说将 e1 的结果作为参数放在消息 m 里,发送给 e0。

在 Ruby 中,所有表达式都是对象,e1 + e2 实际上是 e1.+ e2 的简写法,e1 调用了 方法 +,e2 是这个方法调用的参数。

定义 实例变量、类变量、类常量、类方法

class Foo
  Const = "constant"

  def initialize
    @foo = 1
    @@bar = 2

  def m1
    ...
  end

  def m2 (x,y)
    ...
  end

  def self.classMethod
    ...
  end
end

上例中,@f 为实例变量 (instance variable) ,@@f 为类变量 (class variable) ,Const 是类常量 (Class constant),classMethod 是类方法。

怎么使用类常量和类方法?

Foo::Const
Foo.classMethod

别名 Aliasing

foo = Foo.new
bar = foo

在这个例子中,bar 是 foo 的别名,他们对应是同一个 object,如果 bar 更改了,foo 也会相对应的更改。在 OOP 中,不像函数式编程里数据不可更改,需要特别注意什么时候用别名,什么时候要新建一个 object。

可见性 Visibility

对象内的变量和方法根据不同的定义方式,对象外不一定可见,可以理解为知不知道它的存在。

class Foo
  def initialize
    @foo = 1

  public
  def m1
    ...
  end

  protected
  def m2
    ...
  end

  private
  def m3
    ...
  end
end

foo = Foo.new
foo.@foo # 报错
foo.m1   # 可见
foo.m2   # 报错
foo.m3   # 报错

实例变量 @foo 在 class Foo 外是不知道它的存在的,foo.@foo 会报错,实例变量只有对象内的方法 m1, m2, m3 可以使用。

这样的设计其实很符合面向对象的概念,Foo 是一个对象,要跟这个对象交流,只能通过发送消息,也就是调用方法的方式。

对象内的方法也可以定义可见性。

  • public,表示对外可见,Ruby 对象默认的模式,在类之外可以调用。
  • protected 和 private 只能在 类和子类都能调用。

Getter & Setter

对象的实例变量对外不可见,但是很多情况下可能会需要实例变量,这个时候可以通过定义 Getter 和 Setter 两个方法来实现实例变量的读写。

# getter
def foo @foo
end

# setter
def foo= x
  @foo = x
end

# simpler way to define getters
attr_reader :y, :z # defines getters

# simpler way to define getters and setters
attr_accessor :x # defines getters and setters

反射 Reflections

反射指的是在程序运行的过程中,能访问、检测和修改它本身的这个对象。比如说可以在运行的过程中去访问这个对象里都有哪些方法,然后动态的去调用这些方法。

鸭子类型 Duck Typing

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

def mirror_update point
  point.x = point.x * -1
end

这个方法本意是将一个点 (point) 的 x 值变为 -x,但是所有"像点一样有 x 这个方法"的对象都可以作为参数传入这个函数,这就是所谓的鸭子类型。通常只有动态类型的语言才会支持鸭子类型,但是 Golang 也是有办法实现,这里不展开了。

优缺点

鸭子类型的最大的优点在于代码的重用,同样一段代码,传进的参数只要有那些方法,就能够复用这段代码。但是也带来了缺点,重用使得代码表意不清,我们在用这个方法时,考虑的不是这个方法怎么调用,而是要清楚的了解这个方法的具体实现。举个例子:

def double x
  x + x
end

看到 double 这个函数名,可能最直观的理解就是把数字翻倍,具体的实现 x + x,也可以把两个字符串连在一起。但假如 Ruby 的字符串没有 * 这个方法,具体实现是 2*x 的话,这个函数就不适用于字符串。所以知道一个函数名不够,还得看具体的实现,也挺麻烦的。

Blocks and Using Blocks

这是 Ruby 特有的一个特性。

def silly a
  (yield a) + (yield 42)
end

x.silly(5) {|b| b*2} # 5 * 2 + 42 * 2 = 94

{|b| b*2} 类似于这种的结构,在 Ruby 中是一个 Block,Block 可以放在任何方法调用后,通过方法里的 yeild 来调用 Block 里的内容。上例中,yield a 变成了 a * 2,也就是 5*2。如果一个方法里有 yield,就一定要传入 Block,不然会报错。

子类化和继承

子类化 (SubClassing) 和 继承 (Inheritence) 是 基于类的 OOP 里最重要也是最基础的概念。如果一个类是另一个的子类,那么这个类的实例也是另一个的实例。

class Point
  attr_accessor :x, :y
  def initialize(x,y)
    @x = x
    @y = y end
  def distFromOrigin
    Math.sqrt(@x * @x  + @y * @y)
  end
end
class ColorPoint < Point
  attr_accessor :color
  def initialize(x,y,c="clear")
    super(x,y)
    @color = c
  end
end

在 Ruby 中,子类化用 < 来表示,ColorPoint 是 Point 的子类,它继承了 Point 里所有的方法和变量,所以 ColorPoint 也有 distFromOrigin 的方法,也有 x, y 坐标。可以看出,通过这种继承关系,ColorPoint 不用将 Point 里关于 x, y 及计算到顶点的距离的代码再复制一遍,实现了代码的重用。

覆写和动态调度

覆写 (Overriding) 指的是在子类里和父类一样名字的方法会覆写父类的方法。

动态调度 (Dynamic Patching) 也叫 late binding 或者 virtual methods,指的是从子类去调用父类方法,父类方法里可以动态的调度子类的方法。

class A
  def m1
    self.m2
  end
  def m2
    puts "m2 in A is called"
  end
  def m3
    puts "m3 in A is called"
  end
end

class B < A
  def dynamicDispatch
    self.m1
  end
  def m2
    puts "m2 in B is called"
  end
  def m3
    puts "m3 in B is called"
  end
end

b = B.new
b.m3 # m3 in B is called
b.dynamicDispatch # m2 in B is called

上例中,子类 B 中的方法 m3 覆写了 A 中的 m3,b.dynamicDispatch 调用了 A 中的 m1, m1 里 self.m2的应该调用 A 还是 B 的 方法 m2 呢?因为发起调用的是 b,所以虽然调用的是 A 中 m1 的 self.m2,但是会被动态的调用到 B 中的 m2。

动态调度 VS Closure

对比下吗 ML 和 Ruby 的两个例子:

fun even x = if x=0 then true  else odd  (x-1)
and odd  x = if x=0 then false else even (x-1)

fun even x = (x mod 2)=0
fun even x = false

在 ML 中,有 closure,所以后面定义的两个 even 函数,对第一个函数没有任何影响。

class A
  def even x
    if x==0 then true  else odd  (x-1) end
  end
  def odd x
    if x==0 then false else even (x-1) end
end end
class B < A  # improves odd in B objects
  def even x ; x % 2 == 0 end
end
class C < A  # breaks odd in C objects
  def even x ; false end
end

B.new.odd
C.new.odd

因为动态调度 A 中的 even 不会被调用,而是调用子类 B 和子类 C 的 even。对于程序员来说,可以像 B 一样,写出更好的 even 的代码,也可能像 C 一样写 bug。

可以说动态调度这个特性使得代码更加灵活,重用率可能更高,给了程序员更大的自由度,但是需要程序员去注意不会一不小心写了 bug。

方法查询 Method Lookup

在覆写和动态调度中,涉及到了 OOP 编程语言中怎么去查询方法的设计。在 Ruby 中,对于一个 class C 的实例调用方法 m,方法查询经过下面这些过程:

  1. 在 class C 中找方法 m
  2. 在 class C 中的 mixin (后面介绍) 中找方法 m
  3. 在 class C 的父类中找方法 m
  4. 在 class C 的父类的 mixin 中找方法 m
  5. 以此类推向上查找,直到找到方法 m 的定义,或者找不到返回 method missing error。

多方法 和 静态重载

多方法 (Multimethods) ** 指的是在一个类里,有多个方法有一样的名字,一样数量的参数,但是参数的类型不一样**,在调用这个名字的方法时,会在动态的找到最合适类型的那个方法,进行调用。Ruby 不支持 Multimethods,下面这个例子,借用 Ruby 的语法:

class A
  def addValue Int
    ...
  end
  def addValue String
    ...
  end
  def addValue Rational
    ...
  end
end

在调用时,A.new.addValue ?会根据参数类型,动态的找到最合适的方法。

Clojure 支持 Multimethods。

静态重载 (Static Overload) 和多方法一样,定义的多个方法名字是一样的,但是可以是不同数量的参数和类型。和多方法最大的不同在于,重载不是动态调用时完成的,而是静态的,感觉有点像类型检查,在程序编译时就完成了(不是很确定)。看一个 Java 的例子:

public class Sum {
    // Overloaded sum(). This sum takes two int parameters
    public int sum(int x, int y) {
        return (x + y);
    }

    // Overloaded sum(). This sum takes three int parameters
    public int sum(int x, int y, int z) {
        return (x + y + z);
    }

    // Overloaded sum(). This sum takes two double parameters
    public double sum(double x, double y) {
        return (x + y);
    }
}

多重继承 Multiple Inheritence

多重继承指的是继承多个父类。看一个 C++ 的例子:

struct Base1 {
  void print (void) {
    std::cout << "Base 1" << std::endl;}
};

struct Base2 {
  void print (void) {
    std::cout << "Base 2" << std::endl;}
};

struct Derived1 : public Base1, public Base2 {
  void print (void) { // 重写基类方法
    Base1::print(); // 指定使用何种
    Base2::print();
  }
}

// 原文:https://blog.csdn.net/caroline_wendy/article/details/18077235

多重继承比只能有一个父类的继承要复杂的多,比如多个父类的优先顺序问题,多个父类中,都有同一个方法时,method lookup 要怎么行?类型检查,class 的结构也要相对的复杂的多。所以不是所有的 OOP 都支持多重继承,C++ 是支持的,但 Java 和 Ruby 就没有。

Mixin

因为多继承的复杂性,还有单继承代码复用的局限性,有了一个新的概念可以解决这个问题,mixin。Mixin 是一些方法的合集,可以被包含在 class 里,在这个 class 里可以直接用 mixin 里的方法。

module Doubler
  def double
    self + self # assume included in classes w/ +
  end
end
class String
  include Doubler
end
class Point
  attr_accessor :x, :y
  include Doubler
  def + other
    ans = Point.new
    ans.x = self.x + other.x
    ans.y = self.y + other.y
    ans
end

上例中,Doubler 是一个 Mixin 被用在了 String 和 Point 两个类里,使得两个类都有了 double 这个方法,然后在 class 里可以像 Point 里一样去实现 double 里的 + 方法,来相加两个 Point。再看一个例子:

# you define <=> and you get ==, >, <, >=, <= from the mixin
# (overrides Object's ==, adds the others)
class Name
  attr_accessor :first, :middle, :last
  include Comparable
  def initialize(first,last,middle="")
    @first = first
    @last = last
    @middle = middle
  end
  def <=> other
    l = @last <=> other.last # <=> defined on strings
    return l if l != 0
    f = @first <=> other.first
    return f if f != 0
    @middle <=> other.middle
  end
end

a = Name.new("Tom", "Li")
b = Name.new("Jame", "Chong")
a < b

include Comparable==, >, <, >=, <= 这些方法都包含进去了,在 Comparable 里用 <=> 这个方法返回的结果 -1, 0, 1 来决定 ``==, >, <, >=, <=` 这些返回 true 或者 false。

感觉是一个非常巧妙的做法,让代码的重用蹭蹭蹭往上涨。也很好的解决了在只有一个继承的情况下,怎么去扩展一个 class 的实现。

接口 Interface

接口 (Interface) 是另外一个 OOP 中非常重要的概念,在接口里可以定义一些方法,但是这些方法只有名称、参数类型还有返回类型,没有方法的实现。接口的实现由别的类来完成。

interface Example {
  void   m1(int x, int y);
  Object m2(Example x, String y);
}

class A implements Example {
  public void m1(int x, int y) {...}
  public Object m2(Example e, String s) {...}
}

class B implements Example {
  public void m1(int pizza, int beer) {...}
  public Object m2(Example e, String s) {...}
}

在这个例子中,class A 和 class B 分别实现了 接口 Example,可以看出接口的一个大的优势在于将实现和接口分离,可以很容易的更换实现,而不变接口,从客户端来说,只要知道接口是什么样的,不用管具体的实现。

像 Java 这类静态类型的语言,因为有类型检查,所以不像 Ruby 这种动态类型的语言那样灵活,Interface 的另一个优点是让这个语言有了更大的灵活性。这也是动态类型语言中没有 Interface 的原因之一,因为那些语言本身已经非常灵活了。

抽象方法 Abstract Methods

抽象方法 (Abstract Methods) 是由父类定义了一些没有实现的方法,具体实现由子类 (subclass) 去实现。

abstract class A {
  T1 m1(T2 x) { ... m2(e); ... }
  abstract T3 m2(T4 x);
}
class B extends A {
  T3 m2(T4 x) {
    ...
  }
}

上例中,父类 A 里定义了抽象方法 m2,只有参数类型和返回类型,所有继承了 A 的子类,都需要实现 m2,比如 class B。

抽象方法、接口还有多重继承,通常一门语言里不会都包括这三项。比如 C++ 中,只有抽象方法和多重继承,所以可以定义抽象类,让子类多重继承,抽象类的作用就有点像 interface。

子类型 SubTyping

先看两个类型定义:

1. (int, int, string)
2. (int, int)

在这例子中,1是2的子类型 (SubTyping)。如果一列类型通过省略掉其中零个或者几个类型和另一列类型一模一样,可以说这列类型是另一列类型的子类型。

子类型影响到的是静态类型中的类型检查,如果一个函数传入的参数是要求参数的子类型,类型检查该不该通过?

fun distToOrigin (p:{x:real,y:real}) = ...
fun makePurple (p:{color:string}) = ...
val c :{x:real,y:real,color:string} = {x=3.0, y=4.0, color="green"}
val _ = distToOrigin(c)
val _ = makePurple(c)

这是一个捏造的例子,distToOrigin(c)makePurple(c)该不该报错呢?一门语言该不该接受子类型呢?要在什么样的程度接受子类型?

在 OOP 中,SubClass 的类型其实就是父类的子类型。在 Java 中,是支持子类型的传递,也支持一个 list 子类型的传递,但是会产生一些问题:

class Point { ... }
class ColorPoint extends Point { ... }
...
void m1(Point[] pt_arr) {
  pt_arr[0] = new Point(3,4);
}
String m2(int x) {
  ColorPoint[] cpt_arr = new ColorPoint[x];
  for(int i=0; i < x; i++)
     cpt_arr[i] = new ColorPoint(0,0,"green");
  m1(cpt_arr); // !
  return cpt_arr[0].color; // !
}

在这个例子中,m1(cpt_arr) 是能通过类型检查的,但是在 m1里 ColorPoint 被设置成了 Point,没了 color 这个 attribute,在 cpt_arr[0].color 中就会出错。Java 的处理方式是在虽然通过了类型检查,但是 run-time 自在pt_arr[0] = new Point(3,4) 报错 ArrayStoreException。

更加灵活的类型系统,需要更多更灵活的程序,但是同时能避免的错误也减少了。

另一个很有一些的是 null,在 Java 中本该不是对象,也没有任何的方法,但是却像是所有类型的"子类型",任何对象类型都可以以 null 传入。

泛型 和 子类型

泛型 (Generics) 指的是能够接受任何类型。

class Pair<T1,T2> {
  T1 x;
  T2 y;
  Pair(T1 _x, T2 _y){ x = _x; y = _y; }
  Pair<T2,T1> swap() {
     return new Pair<T2,T1>(y,x);
  }
  ...
}

这里 T1 和 T2 可以是任何类型。在一些情况下,我们可能会要求一个类型是任何子类型的泛型。比如下面这个例子:

List<Point> inCircle(List<Point> pts, Point center, double r) { ... }

List<Point> result = new ArrayList<Point>();
for(Point pt: pts)
  if(pt.distance(center) <= r)
    result.add(pt);
return result;

inCircle 参数和返回的类型是 Point 的 List,如果我们有 Point 的子类 ColorPoint,就得把这个代码复制一遍。为了代码复用,也不能把 List 设置成泛型,因为如果 List 不是 Point 的子类会出问题。有没有办法能实现一个只允许子类型的泛型?这叫做 限定多态 (bounded polymorphism)

<T extends Pt> List<T> inCircle(List<T> pts, Pt center, double r) {
   List<T> result = new ArrayList<T>();
   for(T pt: pts)
     if(pt.distance(center) <= r)
       result.add(pt);
   return result;
}

这里,通过 <T extends Pt> 实现了限定多态。

总结

在这篇文章中,介绍了很多面向对象类语言的基础概念,比如方法/变量的可见性、子类、继承、接口、抽象方法等等。不是每一门语言都包括了所有的这些概念,这里应该也只介绍了多数,不能说所有的概念。但是如果你好好读了这篇文章,以后你在碰到任何一门面向对象的编程语言时,敢说一定有很多相似处,可以让你更快的上手那门语言。通过 子类,多重继承,Mixin,接口,抽象方法等的讨论,也能让你更好的理解那门语言的特性。

这篇文章是这个系列文章的最后一篇。三篇文章分别是对 Coursera Programming Languages 三个部分的总结,介绍了函数式编程和面向对象编程的一些基础概念。这个应该是目前学到的 Coursera 上最棒的课,帮助我深入了解了 函数式编程 和 面向对象编程,虽然平时一直是在面向对象编程,但是从编程语言这个角度去解读现在和以前用过的编程语言,还是学到了许多新内容,认识到了一门编程语言在设计的过程中,都有哪些取舍,能够进一步去思考为什么这么取舍,这么取舍导致了这门语言的什么特性?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编程语言的一些基础概念(三):面向对象 的相关文章

  • 为什么程序员都喜欢安静?

    大家回顾一下上学期间 你在上晚自习想完成今天老师布置的作业 但是你的班级却非常的吵闹 跟置身在菜市场一样 你能专心完成作业吗 不受周围吵闹环境的影响吗 相信大部分的人都难以静下心来认真完成作业 有时候好不容易想到一个思路 结果旁边的人拍你一
  • 如何写一篇简洁易懂的测试报告?

    一 什么是测试报告 测试报告是指把测试的过程和结果写成文档 对发现的问题和缺陷进行分析 为纠正软件的存在的质量问题提供依据 同时为软件验收和交付打下基础 二 测试报告的内容 测试报告的内容可以总结为以下目录 首页 引言 目的 背景 缩略语
  • 程序员如何做副业?35岁前,千万别让死工资绊住你赚钱的步伐

    近年来互联网行情下降 好多人都在思考要不要搞个副业来抵御风险 这不又来事了 这两天又爆了互联网大裁员 继阿里 向社会输送人才 之后 京东又搞了个 毕业礼 整的小伙伴们人心惶惶 副业的关注度又一波升级 那今天我们就来聊聊 程序员做副业这件事
  • java 中的指针_Java中的指针

    java 中的指针 Java中有指针吗 简短的答案是 不 没有 这对于许多开发人员来说似乎是显而易见的 但是 为什么对其他人却不那么明显呢 http stackoverflow com questions 1750106 how can i
  • 每日一问:你想如何破坏单例模式?

    前言 1 单例是什么 单例模式 是一种创建型设计模式 目的是保证全局一个类只有一个实例对象 分为懒汉式和饿汉式 所谓懒汉式 类似于懒加载 需要的时候才会触发初始化实例对象 而饿汉式正好相反 项目启动 类加载的时候 就会创建初始化单例对象 1
  • Python基础语法学习之变量与赋值

    近几年Python飞速发展 开始学习Python的人群不在仅仅局限于编程开发者 许多其他行业的从业者也开始将Python作为自己的职业技能 本文仍然是针对零基础的初学者 继续学习Python的基础语法 变量与赋值 主要内容包括变量和赋值的概
  • python列表增加元素的三种方法

    本文主要介绍了关于python列表增加元素的几种操作方法 主要有insert方法 extend方法和append方法 每种方法给大家介绍的非常详细 希望对你有所帮助 1 insert方法 该方法包含两个参数 第一个参数为插入的位置参数 第二
  • 电力行业数字孪生技术应用白皮书(2022)

    白皮书从产学研用多视角出发 结合电力行业的特性 分析阐述了数字孪生概念 核心技术 应用价值以及数字孪生电网标准体系 从数字感知 混合建模 高效仿真 可视化和虚实迭代等不同方面介绍了数字孪生的支撑技术以及应用现状 梳理了当前电力行业数字孪生技
  • pymongo "ServerSelectionTimeoutError: No servers found yet" 错误的解决

    系统转移过程中 擅自把aptitude安装的mongoengine换成了pip安装 系统启动以后 报这个错误 报错提示 File usr local lib python2 7 dist packages pymongo mongo cli
  • 【NLP】自然语言处理技术在自动生成足球比赛战报上的应用

    1 背景介绍 自动生成新闻看似是一个很成熟的技术 很多年前就有各种应用 但是深入了解后我们可以发现机器自动生成的文章一般都是复述一些数字和简单的趋势变化 所以自动生成新闻的技术广泛应用在金融 体育领域 原因就是这类报道需要基于一定的事实 而
  • 去国企1年后,我后悔了!重回大厂内卷

    文章来源 cnblogs com peiyu1988 html 01 前言 2019年初 我通过一整天的笔试及面试加入一家 某一线城市国资委全资控股 某集团的研究机构 中央研究院 任职高级软件工程师 中级职称 在这边工作了整整一年 目前已经
  • 推荐一篇详细的Nginx 配置清单

    Nginx 是一个高性能的 HTTP 和反向代理 web 服务器 同时也提供了 IMAP POP3 SMTP 服务 其因丰富的功能集 稳定性 示例配置文件和低系统资源的消耗受到了开发者的欢迎 本文 我们总结了一些常用的 Nginx 配置代码
  • python字典中如何添加键值对

    添加键值对 首先定义一个空字典 gt gt gt dic 直接对字典中不存在的key进行赋值来添加 gt gt gt dic name zhangsan gt gt gt dic name zhangsan 如果key或value都是变量也
  • 手机知识:手机的快充技术是什么,看完本文你就懂了

    目录 1 什么是手机快充 2 目前主流的手机快充协议 2 1 PD协议 2 2 PE协议 联发科 2 3 QC协议 高通 2 4 VOOC闪充 OPPO厂商 2 5 SCP FCP闪充 华为厂商 2 6 FlashCharge闪充 Vivo
  • Python 程序设计习题(4) —— 列表与元组

    目录 1 Python 习题部分 2 Python 习题讲解 列表 元组 其他 1 Python 习题部分 要想学习一门语言 便少不了练习 故附上部分 Python 习题 供大家学习参考 如有错误之处 还望指正 1 二年级一班举行了数学考试
  • C++ 智能指针详解

    点击蓝字 关注我们 参考资料 C Primer中文版 第五版 我们知道除了静态内存和栈内存外 每个程序还有一个内存池 这部分内存被称为自由空间或者堆 程序用堆来存储动态分配的对象即那些在程序运行时分配的对象 当动态对象不再使用时 我们的代码
  • C#有像Java ThreadLocal的类似实现吗?

    在C 中 可以使用 ThreadLocal
  • 面向对象编程---基于java控制台的高校教材管理系统课设

    功能要求 1 实现出版社 教材类型的管理 2 实现教材的订购管理 3 实现教材的入库管理 4 创建规则 实现教材的书号必须满足以ISBN开头 后跟10个数字 比如ISBN7302120363 5 创建触发器 实现教材入库和出库时自动修改库存
  • 面向对象编程---基于Javaswing的医院管理系统课设

    S2021003基于Javaswing的医院管理系统 https www bilibili com video BV1e34y1Z75L share source copy web vd source 3d18b0a7b9486f50fe7
  • Python - 字典4

    复制字典 您不能简单地通过输入 dict2 dict1 来复制一个字典 因为 dict2 只会成为 dict1 的引用 对 dict1 的更改也会自动应用于 dict2 有多种方法可以复制字典 一种方法是使用内置的 copy 方法 示例 使

随机推荐

  • firewall 整合 docker:指定 ip 请求 docker

    知识准备 主要演示的是 centos7 的 firewall firewall 是 centos7 的默认自带防火墙 firewall 的底层是 iptables firewall 中的 chain 可以理解为是防火墙的策略组 docker
  • Vue 项目中使用ElementUI 框架中message 对话框实例对象

    Element ui中message弹出框使用总结 第一 按需导入的element ui中的message 在customer vue 文件中引入message 弹出对话框 第二步 在方法里面直接通过调用Message 弹出对话框实例对象
  • 【软件测试】

    系列文章目录 文章目录 系列文章目录 前言 第四章 单元测试 4 1 软件测试过程概述 4 2 什么是单元测试 4 2 1 单元测试的定义 4 2 2 单元测试的重要性 4 2 3 单元测试原则 4 3 单元测试的目标和任务 4 3 1 单
  • 开发工程师VS测试工程师VS测试开发工程师

    每年正式上班之后就会非常忙 今年也不例外 我们公司现在也忙了起来 都没有时间写我的自动化测试教程了 不过大家放心 我会继续写下去的 不过可能更新的不那么快了 最近被同事问到了一个问题 开发 测试和测试工程师都有啥区别 开发转测试是不是比我们
  • ISP之LSC(Lens Shading Correction)

    LSC Lens Shading Correction即镜头暗影校正 一 LSC的意义 众所周知Lens Shading分为Luma Shading和Color Shading 一般来说 物体到Lens中心的距离越远 图像越暗 呈圆形中性对
  • NC 和NCC 用户被锁定

    NC账户被锁定 NC上的用户 不管是管理员 还是用户 都是统一存放在同一张表的 不像NCC 一样有sm super user这张表 用来区分 所以在NC上一视同仁就好了 以下有两种常用的修改方式 一 数据库修改 nc65用户被锁定后涉及到三
  • 【Nginx】Nginx新增自定义模块

    Nginx新增自定义模块 系统环境 Nginx模块分类 Nginx模块执行流程 Nginx Handler模块示例 Nginx filter模块示例 系统环境 uname a Linux localhost localdomain 3 10
  • 爬虫 - QS世界大学排名数据

    爬虫 QS世界大学排名数据 网站简介 爬虫方法概述 使用工具 爬虫概述 第一部分 导入需要用到的python包 设置selenium控制浏览器打开网页 控制鼠标操作 定位节点 提取数据 滚轮翻页 构建循环自动爬取数据 数据储存 第二部分 导
  • 【图像处理OpenCV(C++版)】——2.2 OpenCV之矩阵运算详解(全)

    前言 欢迎来到本博客 本专栏主要结合OpenCV和C 来实现一些基本的图像处理算法并详细解释各参数含义 适用于平时学习 工作快速查询等 随时更新 具体食用方式 可以点击本专栏 OpenCV快速查找 更新中 gt 搜索你要查询的算子名称或相关
  • Mac 终端进入 conda 虚拟环境后 pip 依然安装到全局下的问题解决

    一 问题起因 之前折腾安装各种软件 可能是不小心改了些什么莫名奇妙的设置 然后就出现了问题 mac 系统 Catalina 版本10 15 5 在 anaconda 中创建了新的虚拟环境 比如 test 然后在 mac 终端中 输入sour
  • HC32F003系列芯片时钟源性能测试及分析

    HC32F003系列芯片时钟源性能测试及分析 测试概要 测试目的 分析HC32F003系列芯片几种时钟源的性能差异 主要分析频率 占空比的误差范围 测试项目 分别测试以下几种时钟源的性能 每种测试不少于10次 内部高速4MHz 内部高速8M
  • 服务器网络请求返回状态码集合

    在开发过程中报错是最令人头疼的 接下来我们就来谈谈那些状态码都是什么 200 服务器成功返回请求的数据 201 新建或修改数据成功 202 一个请求已经进入后台排队 异步任务 204 删除数据成功 400 发出的请求有错误 服务器没有进行新
  • 5G全产业链最新解读

    来源 中创产业研究院 摘要 自5G概念的提出 各国相关技术的研发以及产业布局也在如火如荼进行之中 与此同时我国5G在标准研发上正逐渐成为全球领跑者 有望在2019年实现5G技术的试商用 在2020年实现正式商用 本文将围绕5G的概况 国内外
  • pyinstaller在x86环境安装与多文件打包

    一 安装 Python官网下载安装源码 或者使用pip install pyinstaller安装 源码安装 解压后 进入文件夹 执行 python setup py install进行安装 二 多文件打包 方法主要还是两个 1 还是直接使
  • 6. Redis缓存设计与性能优化

    分布式缓存技术Redis 1 多级缓存架构 2 缓存设计 2 1 缓存穿透 2 2 缓存失效 击穿 2 3 缓存雪崩 2 4 热点缓存key重建优化 2 5 缓存与数据库双写不一致 3 开发规范与性能优化 3 1 键值设计 3 1 1 ke
  • 数据库系统笔记1: 绪论

    数据库概述 DBMS Data Base Management System 数据库管理系统 Metadata 元数据 关于数据描述的数据 数据模型 层次模型 使用树状结构表示实体和实体之间的联系 网状模型 使用有向图表示实体和实体之间的联
  • Iterator、Iterable接口的使用及详解

    Java集合类库将集合的接口与实现分离 同样的接口 可以有不同的实现 Java集合类的基本接口是Collection接口 而Collection接口必须继承java lang Iterable接口 以下图表示集合框架的接口 java lan
  • MFE常用数据结构之Lattice

    今天看了DUFFY的C For FE中关于介绍Lattice的相关内容 为了表示对原作者的尊敬 首先我还是引用一下作者关于Lattice Structures的介绍的原话 Lattice structures are well known
  • 软件工程基础知识--软件过程模型

    软件过程模型习惯上也称为软件开发模型 它是软件开发全部过程 活动和任务的结构框架 典型的软件过程模型有瀑布模型 增量模型 演化模型 原型模型 螺旋模型 喷泉模型 基于构件的开发模型和形式化方法模型等 瀑布模型 该模型给出了软件生存周期各阶段
  • 编程语言的一些基础概念(三):面向对象

    在前面两篇中 主要讲了函数式编程语言的一些基础概念 这篇是 Coursera Programming Languages Part C 的总结 通过 Ruby 介绍面向对象编程里的一些概念 了解这些概念能让你在上手任何一门新的面向对象语言时