GO(C、C++、Python)语言的认识(一)

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

go语言有类的概念吗

Go 语言中没有传统面向对象编程(如 Java、C++)中的 “类”(Class)概念,但它通过结构体(Struct)和方法(Method)的结合,实现了类似类的功能。

结构体(Struct):可以理解为 “类” 的数据部分,用于定义属性(字段)。例如:

type Person struct {
    Name string
    Age  int
}

方法(Method):可以为结构体绑定函数,相当于 “类” 的方法。例如:

func (p Person) Greet() string {
    return "Hello, my name is " + p.Name
}

通过这种方式,结构体 + 方法的组合能够实现类的封装、数据与行为绑定等核心特性,只是语法上没有使用 “类” 这一关键字。

如何使用Go语言的类(结构体+方法)?

在 Go 语言中,使用 “结构体 + 方法” 实现类似类的功能,主要包括定义结构体为结构体绑定方法创建实例访问字段和调用方法等步骤,以下是具体用法:

1. 定义结构体(类似 “类的属性”)

结构体用于封装数据(字段),相当于类中的成员变量。

// 定义一个 Person 结构体,包含 Name 和 Age 两个字段
type Person struct {
    Name string // 姓名
    Age  int    // 年龄
}

2. 为结构体绑定方法(类似 “类的方法”)

通过 “方法接收器” 为结构体绑定函数,这些函数就是结构体的方法,用于定义结构体的行为。

// 为 Person 结构体绑定 Greet 方法(值接收器:不修改原结构体数据)
func (p Person) Greet() string {
    return "Hello, I'm " + p.Name + ", " + strconv.Itoa(p.Age) + " years old."
}

// 为 Person 结构体绑定 SetAge 方法(指针接收器:修改原结构体数据)
func (p *Person) SetAge(newAge int) {
    p.Age = newAge // 指针接收器可以修改结构体的字段值
}
  • 值接收器p Person):方法内部操作的是结构体的副本,不会影响原实例。
  • 指针接收器p *Person):方法内部操作的是结构体的指针,会直接修改原实例。

3. 创建结构体实例(类似 “实例化对象”)

通过 var 声明、直接赋值或 new() 函数创建结构体实例。

func main() {
    // 方式1:直接初始化
    p1 := Person{Name: "Alice", Age: 25}

    // 方式2:先声明再赋值
    var p2 Person
    p2.Name = "Bob"
    p2.Age = 30

    // 方式3:通过 new() 创建(返回指针)
    p3 := new(Person)
    p3.Name = "Charlie" // 指针可以直接访问字段(语法糖)
    p3.Age = 35
}

4. 访问字段和调用方法

通过实例(或指针)直接访问字段、调用方法。

func main() {
    p := Person{Name: "Alice", Age: 25}

    // 访问字段
    fmt.Println("Name:", p.Name) // 输出:Name: Alice

    // 调用方法(值接收器方法)
    fmt.Println(p.Greet()) // 输出:Hello, I'm Alice, 25 years old.

    // 调用方法(指针接收器方法,修改年龄)
    p.SetAge(26)
    fmt.Println(p.Age) // 输出:26
}

总结

  • 结构体(struct)相当于 “类” 的属性定义,用于存储数据。
  • 方法(通过接收器绑定到结构体)相当于 “类” 的方法,用于定义行为。
  • 指针接收器用于修改结构体实例的数据,值接收器用于只读操作。

这种方式虽然没有 “类” 的关键字,但能实现类的核心功能(封装、数据与行为绑定)。

那go有继承和多态的概念吗

Go 语言没有传统面向对象(如 Java、C++)中 “继承” 和 “多态” 的严格概念,但通过其独特的语法设计(如结构体嵌套接口),可以实现类似的功能。

1. 类似 “继承” 的实现:结构体嵌套

Go 不支持类的继承,但可以通过结构体嵌套(匿名字段)实现 “属性和方法的复用”,类似继承的效果。

示例:
// 定义一个基础结构体 Animal
type Animal struct {
    Name string
}

// 为 Animal 绑定方法
func (a *Animal) Eat() {
    fmt.Printf("%s is eating\n", a.Name)
}

// 定义 Dog 结构体,嵌套 Animal(匿名字段)
type Dog struct {
    Animal // 嵌套 Animal,相当于“继承”了 Animal 的字段和方法
    Breed  string // Dog 自己的字段
}

// 为 Dog 绑定自己的方法(可以“覆盖”基础方法)
func (d *Dog) Bark() {
    fmt.Printf("%s is barking\n", d.Name)
}

func main() {
    // 创建 Dog 实例
    dog := &Dog{
        Animal: Animal{Name: "Buddy"},
        Breed:  "Golden Retriever",
    }

    // 调用从 Animal 继承的字段和方法
    dog.Eat() // 输出:Buddy is eating

    // 调用 Dog 自己的方法
    dog.Bark() // 输出:Buddy is barking
}
  • 嵌套的结构体(如 Animal)的字段和方法,会被 “提升” 到外层结构体(如 Dog)中,可直接访问,类似 “继承” 的属性和方法复用。
  • 若外层结构体定义了与嵌套结构体同名的方法,会 “覆盖” 嵌套的方法,类似 “方法重写”。

2. 类似 “多态” 的实现:接口(Interface)

Go 的接口是隐式实现的(无需显式声明 “implements”),只要一个类型实现了接口的所有方法,就属于该接口类型。这种特性可以实现类似多态的效果:同一接口类型的变量,可以存储不同的具体类型实例,并调用其方法。

示例:
// 定义一个接口 Mover,包含 Move 方法
type Mover interface {
    Move()
}

// 定义 Dog 类型,实现 Mover 接口
type Dog struct {
    Name string
}

func (d *Dog) Move() {
    fmt.Printf("%s is running\n", d.Name)
}

// 定义 Bird 类型,实现 Mover 接口
type Bird struct {
    Name string
}

func (b *Bird) Move() {
    fmt.Printf("%s is flying\n", b.Name)
}

func main() {
    // 声明一个 Mover 接口变量
    var m Mover

    // 赋值为 Dog 实例(Dog 实现了 Mover)
    m = &Dog{Name: "Buddy"}
    m.Move() // 输出:Buddy is running

    // 赋值为 Bird 实例(Bird 实现了 Mover)
    m = &Bird{Name: "Tweety"}
    m.Move() // 输出:Tweety is flying
}
  • 接口 Mover 定义了抽象行为 Move()Dog 和 Bird 都实现了该方法,因此都属于 Mover 类型。
  • 同一接口变量 m 可以接收不同类型的实例,并调用其 Move() 方法,表现出不同的行为,这就是类似多态的效果。

总结

Go 没有传统的 “继承”,但通过结构体嵌套实现了属性和方法的复用,类似继承。 Go 没有传统的 “多态”,但通过接口的隐式实现,允许同一接口类型操作不同具体类型,实现了类似多态的行为。

这种设计体现了 Go 的简洁性,避免了继承带来的复杂层次关系,更强调 “组合优于继承”。

C语言可以实现类似的这些功能吗?

C 语言虽然是面向过程的语言,没有内置的类、继承、多态等面向对象特性,但可以通过一些技巧模拟类似功能,核心是利用结构体函数指针指针类型转换实现封装、复用和动态行为。

    1. 模拟 “类”(封装)

    C 语言用结构体存储数据(类似 “属性”),用函数指针或普通函数(接收结构体指针作为参数)模拟 “方法”,实现数据与行为的绑定。

    #include <stdio.h>
    #include <stdlib.h>
    
    // 定义“类”的结构体(存储数据)
    typedef struct {
        char* name;
        int age;
    } Person;
    
    // 构造函数:初始化实例
    Person* Person_create(char* name, int age) {
        Person* p = malloc(sizeof(Person));
        p->name = name;
        p->age = age;
        return p;
    }
    
    // 方法:打印信息
    void Person_greet(Person* p) {
        printf("Hello, I'm %s, %d years old.\n", p->name, p->age);
    }
    
    // 析构函数:释放资源
    void Person_destroy(Person* p) {
        free(p);
    }
    
    int main() {
        Person* p = Person_create("Alice", 25);
        Person_greet(p); // 调用“方法”
        Person_destroy(p);
        return 0;
    }

    2. 模拟 “继承”(复用)

    通过结构体嵌套(将 “父类” 结构体作为 “子类” 结构体的第一个成员),实现数据和方法的复用,类似继承。

    // 父类:Animal
    typedef struct {
        char* name;
    } Animal;
    
    void Animal_eat(Animal* a) {
        printf("%s is eating.\n", a->name);
    }
    
    // 子类:Dog(嵌套 Animal 作为第一个成员)
    typedef struct {
        Animal animal; // 模拟“继承”Animal
        char* breed;   // 子类自己的属性
    } Dog;
    
    // 子类方法: bark
    void Dog_bark(Dog* d) {
        printf("%s is barking.\n", d->animal.name);
    }
    
    int main() {
        Dog* dog = malloc(sizeof(Dog));
        dog->animal.name = "Buddy"; // 复用父类的属性
        dog->breed = "Golden Retriever";
    
        Animal_eat((Animal*)dog); // 调用父类方法(通过指针转换)
        Dog_bark(dog);            // 调用子类方法
    
        free(dog);
        return 0;
    }

    3. 模拟 “多态”(动态行为)

    通过函数指针表(虚函数表) 实现多态:不同 “子类” 结构体中存储不同的函数指针,调用时根据实际类型执行对应实现。

    // 定义接口(函数指针表)
    typedef struct {
        void (*move)(void*); // 函数指针,接收通用指针
    } Mover;
    
    // 子类1:Dog
    typedef struct {
        Mover mover; // 包含接口
        char* name;
    } Dog;
    
    void Dog_move(void* self) {
        Dog* dog = (Dog*)self;
        printf("%s is running.\n", dog->name);
    }
    
    // 子类2:Bird
    typedef struct {
        Mover mover; // 包含接口
        char* name;
    } Bird;
    
    void Bird_move(void* self) {
        Bird* bird = (Bird*)self;
        printf("%s is flying.\n", bird->name);
    }
    
    // 通用函数:接收接口类型,调用对应方法
    void move(Mover* m, void* self) {
        m->move(self); // 动态调用实际类型的方法
    }
    
    int main() {
        Dog dog = {{Dog_move}, "Buddy"};
        Bird bird = {{Bird_move}, "Tweety"};
    
        move(&dog.mover, &dog);   // 输出:Buddy is running
        move(&bird.mover, &bird); // 输出:Tweety is flying
        return 0;
    }
    • 不同结构体实现同一函数指针(接口),调用时通过接口的函数指针动态执行对应实现,模拟多态。

    总结

    C 语言可以通过结构体 + 函数指针 + 指针转换模拟类、继承和多态,但需要手动实现所有逻辑(如构造 / 析构、类型转换、函数指针绑定等),没有语言层面的自动支持,代码会更繁琐,且缺乏编译期类型检查(容易出错)。相比之下,Go 或其他面向对象语言的实现更简洁、安全。

    为什么说go实现这些功能就更简洁、安全呢?

    Go 语言在实现类、继承、多态等功能时更简洁、安全,核心原因是它通过语言层面的原生设计(而非开发者手动模拟),简化了逻辑并提供了编译期检查,避免了 C 语言中手动处理的繁琐和风险。具体体现在以下几点:

    1. 模拟 “类”:更简洁的结构体 + 方法绑定

    • Go 的优势
      Go 直接支持 “结构体 + 方法” 的语法,方法可以通过 “接收者” 显式绑定到结构体,无需像 C 那样用函数指针或参数传递结构体指针。
      例如:

      type Person struct {
          name string
          age  int
      }
      
      // 方法直接绑定到 Person,语法清晰
      func (p Person) Greet() {
          fmt.Printf("Hello, I'm %s\n", p.name)
      }
      
       

      调用时直接写 p.Greet(),无需像 C 那样手动传递 Person* 指针(如 Person_greet(p)),代码更直观。

    • 安全性
      Go 的方法调用有明确的接收者类型,编译期会检查调用者是否匹配,避免 C 中因函数参数类型错误导致的隐蔽 bug(比如传递错误的结构体指针)。

    2. 模拟 “继承”:通过组合 + 匿名字段简化复用

    • Go 的优势
      Go 用 “组合” 替代传统继承,通过匿名字段实现结构体的属性和方法复用,无需像 C 那样依赖 “结构体嵌套 + 指针强制转换”。
      例如,让 Dog 复用 Animal 的功能:

      type Animal struct {
          name string
      }
      
      func (a Animal) Eat() {
          fmt.Printf("%s is eating\n", a.name)
      }
      
      // 匿名嵌入 Animal,自动获得其属性和方法
      type Dog struct {
          Animal // 匿名字段,类似“继承”
          breed  string
      }
      
      func main() {
          dog := Dog{Animal: Animal{name: "Buddy"}, breed: "Golden"}
          dog.Eat() // 直接调用 Animal 的方法,无需手动转换指针
      }
      
       

      相比 C 中需要将 Dog* 强制转为 Animal* 才能调用父类方法,Go 的匿名字段让复用更自然,无需开发者手动处理类型转换。

    • 安全性
      匿名字段的方法调用由编译器自动解析,避免了 C 中指针强制转换可能导致的内存访问错误(比如转换后访问越界字段)。

    3. 模拟 “多态”:接口的隐式实现更简洁安全

    • Go 的优势
      Go 的接口是 “隐式实现” 的 —— 只要结构体实现了接口的所有方法,就自动属于该接口类型,无需像 C 那样手动维护函数指针表。
      例如:

      // 接口
      type Mover interface {
          Move()
      }
      
      // Dog 实现 Mover
      type Dog struct{}
      func (d Dog) Move() { fmt.Println("Dog runs") }
      
      // Bird 实现 Mover
      type Bird struct{}
      func (b Bird) Move() { fmt.Println("Bird flies") }
      
      // 多态函数:接收 Mover 接口
      func LetItMove(m Mover) {
          m.Move() // 自动调用实际类型的方法
      }
      
      func main() {
          LetItMove(Dog{}) // 输出:Dog runs
          LetItMove(Bird{}) // 输出:Bird flies
      }
      

      相比 C 中需要手动定义函数指针表、传递 void* 并强制转换类型,Go 的接口完全由编译器处理类型匹配,代码更简洁,且无需手动维护函数指针绑定。

    • 安全性
      编译器会检查结构体是否实现了接口的所有方法,避免 C 中因函数指针未正确绑定导致的运行时错误(比如调用空指针函数)。

    总结

    Go 并非严格的面向对象语言,但它通过结构体 + 方法 + 接口的原生设计,在实现类、继承、多态等功能时:

    • 简洁性:省去了 C 中手动处理函数指针、指针转换、结构体嵌套的繁琐代码,语法更直观;
    • 安全性:依赖编译器的类型检查,避免了 C 中手动操作指针和函数指针可能导致的内存错误或逻辑漏洞。

    因此,Go 的实现方式比 C 更高效、可靠。

    所以go是面向对象语言还是面向过程语言?

    Go 语言既不是纯粹的面向对象语言,也不是纯粹的面向过程语言,它更像是一种融合了多种编程范式的语言,同时支持面向对象和面向过程的编程风格,且更强调简洁性和实用性。

    具体来说:

    • 它不严格遵循传统面向对象语言(如 Java、C++)的 “类、继承、多态” 三大特性,比如没有 “类” 的关键字,用 “结构体 + 方法” 模拟类;用 “组合” 替代传统继承;用 “接口” 实现多态(且是隐式实现)。
    • 但它支持面向对象的核心思想,比如封装(结构体字段可控制访问权限)、多态(通过接口)、代码复用(通过组合)。
    • 同时,它也保留了面向过程的简洁性,比如支持函数作为一等公民、流程控制语句等,适合编写简洁的命令式代码。

    因此,Go 更倾向于 “实用主义”,不被单一范式束缚,而是根据问题场景灵活选择编程风格。

      开发板推荐:天空星STM32F407VET6开发板

      超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

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

      抵扣说明:

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

      余额充值