【C++】继承—C++的秘密武器,get父类的智慧

【C++】继承—C++的秘密武器,get父类的智慧
在这里插入图片描述
✨ 坚持用清晰易懂的图解+代码语言, 让每个知识点都简单直观!
🚀 个人主页不呆头 · ZEEKLOG
🌱 代码仓库不呆头 · Gitee
📌 专栏系列 :📖 《C语言》🧩 《数据结构》💡 《C++》🐧 《Linux》💬 座右铭 :“不患无位,患所以立。”

【C++】继承—C++的秘密武器,父类的智慧你get了吗?


摘要

C++继承是通过派生类继承基类的成员,避免了代码冗余,提升了代码复用性。继承有public、protected和private三种方式,影响成员的访问权限。继承支持多态性,使得同一接口能表现出不同的行为。继承和组合各有优缺点,继承适用于“is-a”关系,而组合适用于“has-a”关系。在使用时需根据需求选择合适的方式,避免过度依赖继承,保持代码的可维护性。


目录

一、继承的概念和定义

1. 继承的概念

继承机制是面向对象程序设计使代码可以复用的重要手段。它允许陈煦园在保持原有类特性的基础上进行扩展,增加功能,产生新的类,这个新的类就叫做派生类。继承体现了面向对象程序设计的层次结构,体现了由简单到复杂的过程。以前我们所接触的复用都是函数层次的复用(函数调用,模板使用)。现在的继承是类层次设计的复用。
  1. 老师和学生共享“人”的属性,继承帮你避免重复劳动
    在面向对象编程中,继承就像是你从父母那里继承了某些特征。比如,老师和学生都是人,都有姓名、年龄、身高、体重等共同属性,但如果每次都重复写这些属性岂不是太麻烦?所以,我们创建一个Person类,像是一个“通用模板”,包含所有人的共有属性,老师和学生就可以从这个类继承所有这些基本信息,轻松避免代码冗余。
  2. 差异化部分各自发挥,老师和学生各有专属功能
    当然,老师和学生并不是完全相同的,他们有自己的特征和行为。比如,老师有职称,学生有学号;老师会授课,学生会学习。通过继承,Person类只负责提供基本的属性,而各自的特有属性和功能(如授课学习)则由老师和学生各自负责,这样既不重复也能满足不同需求。
#include<iostream>#include<string>usingnamespace std;classPerson{public:voidPrint(){ cout <<"姓名: "<< _name << endl; cout <<"年龄: "<< _age << endl;}protected:int _age =20; string _name ="DaiTou";};classStudent:publicPerson{public:voidStudy(){ cout << _name <<" 正在学习中..."<< endl;}protected:int _stuid =2025101010;};classTeacher:publicPerson{public:voidTeach(){ cout << _name <<" 正在授课中..."<< endl;}protected: string _title ="教授";};intmain(){ Student s; s.Print(); s.Study(); Teacher t; t.Print(); t.Teach();return0;}
我们来看这段代码,这其中属于老师和人的公共属性有年龄,和人名,属于他们各自的自己的属性有学生的学号和老师的职称。

2. 继承的定义

2.1 定义格式

上述中Person类就是父类,也叫做基类;Student类和Teacher类叫做子类,也叫做派生类。

继承方式: public继承,protected继承,private继承。
继承使用方法: class 子类名 :继承方式 父类名


2.2 继承基类成员访问方式的变化
类成员 / 继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见
基类的private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员虽然被继承到了派生类对象,但是语法限制派生类对象无论在类里面还是在类外面都不可以访问基类的私有成员。这个基类的private成员和派生类中原有的private成员不同,派生类原有的private成员可以在类里面进行访问,但是在类外面不能访问。如果我们不想基类的成员在派生类继承后直接在派生类的类外面直接访问,但在派生类的类里面进行访问,我们就可以直接把基类的成员定义成protected成员。因为protected成员在基类中的里面可以访问,在基类的外面不可以访问(protected保护成员限定符是因为继承出现的)基类的成员(除开private成员)在派生类的继承中,取权限大小的那一个public > protected > private如果没用继承方式的话,使用class就默认private继承;使用struct就默认public继承。

3. 继承类模板
#include<iostream>#include<vector>usingnamespace std;//=========================// 自定义命名空间 dh// 防止与标准库或其他代码命名冲突//=========================namespace dh {//=========================// 类模板:stack<T>// 模拟栈(stack)的实现// 继承自 std::vector<T>,利用其底层动态数组特性//=========================template<classT>classstack:public std::vector<T>// 继承标准库 vector<T>{public:// 入栈操作:在 vector 尾部插入元素voidpush(const T& x){vector<T>::push_back(x);}// 出栈操作:删除 vector 尾部元素voidpop(){vector<T>::pop_back();}// 获取栈顶元素:返回 vector 的最后一个元素的引用 T&top(){returnvector<T>::back();}// 判断栈是否为空boolempty(){returnvector<T>::empty();}};}//=========================// 主函数入口//=========================intmain(){ dh::stack<int> st;// 定义一个整型栈对象 st.push(1);// 压入元素 1 st.push(2);// 压入元素 2// 循环打印并弹出栈顶元素while(!st.empty()){ cout << st.top()<<" ";// 输出当前栈顶元素 st.pop();// 弹出栈顶元素}return0;}

二、 基类和派生类对象的赋值转换

public继承的派生类对象可以赋值给基类的对象/基类的指针/积累的引用。其中当派生类的对象赋值给基类的对象是,派生类中多出来的成员会被切掉,这叫做切片。派生类中基类的那一部分进行赋值。但是当用基类指针或引用指向派生类对象时,并不会发生切片,只是“看不到”派生类新增的部分,但派生类对象仍然完整地存在内存中。这时通过虚函数可以实现多态访问。
classBase{public:int a;};classDerived:publicBase{public:int b;};intmain(){ Derived d; d.a =1; d.b =2; Base b = d;// ❌ 发生对象切片 cout << b.a << endl;// 输出 1// b 中没有 b.b 这个成员,派生部分被“切掉”了}
Derived d; Base* pb =&d;// ✅ 没有切片 Base& rb = d;// ✅ 没有切片

此时 pb 和 rb 只是“看不到”派生类新增的部分,
但派生类对象仍然完整地存在内存中。
这时通过虚函数可以实现多态访问。

基类对象不可以赋值给派生类对象
在这里插入图片描述
基类的指针或引用可以通过强制类型转换赋值给派生类的指针或引用。因为基类的指针或者引用是有可能指向派生类的父类那一部分。但是必须是基类的指针或者引用指向派生类的对象才是安全的。
在这里插入图片描述
#include<iostream>#include<string>usingnamespace std;//基类classPerson{public:voidPrint(){ cout <<"姓名: "<< _name << endl; cout <<"年龄: "<< _age << endl;}protected:int _age =20; string _name ="DaiTou";};//派生类classStudent:publicPerson{public:voidStudy(){ cout << _name <<" 正在学习中..."<< endl;}protected:int _stuid =2025101010;};classTeacher:publicPerson{public:voidTeach(){ cout << _name <<" 正在授课中..."<< endl;}protected: string _title ="教授";};intmain(){ Student s; Teacher t;// ======= 基类指针指向派生类对象 ======= Person* p1 =&s;// 基类指针指向 Student 对象 Person* p2 =&t;// 基类指针指向 Teacher 对象 cout <<"基类指针访问派生类对象(Student):"<< endl; p1->Print();// ✅ 可以访问基类成员函数// p1->Study(); // ❌ 错误:基类指针不能访问派生类独有成员 cout << endl <<"基类指针访问派生类对象(Teacher):"<< endl; p2->Print();// p2->Teach(); // ❌ 同理,不可访问派生类特有函数 cout << endl;// ======= 基类引用指向派生类对象 ======= Person& rp1 = s;// 基类引用绑定到 Student Person& rp2 = t;// 基类引用绑定到 Teacher cout <<"基类引用访问派生类对象(Student):"<< endl; rp1.Print();// ✅ 可以调用基类成员 cout << endl <<"基类引用访问派生类对象(Teacher):"<< endl; rp2.Print();// ✅ 依然可以访问基类成员return0;}
在这里插入图片描述

三、继承中的作用域

1. 隐藏规则

  1. 在继承体系中的基类和派生类都有其独立的作用域。
  2. 当派生类和基类中有同名成员时,派生类中的同名成员将屏蔽基类中的同名成员,直接对当前派生类的同名成员进行访问,这种情况叫做隐藏,也叫做重定义(在派生类的成员函数中,可以使用 基类::同名成员 的形式直接访问基类的同名成员)。
  3. 注意,如果是派生类和基类中的成员函数构成隐藏的话,只要函数名相同就构成隐藏。
  4. 但是注意,在实际的继承体系中,最好不要定义同名成员。
#include<iostream>#include<string>usingnamespace std;// 基类classPerson{public:voidPrint(){ cout <<"基类:年龄:"<< _age << endl; cout <<"基类:姓名:"<< _name << endl;}protected:int _age =20; string _name ="DaiTou";};// 派生类classStudent:publicPerson{public:voidPrint(){ cout <<"派生类:年龄:"<< _age << endl; cout <<"派生类:姓名:"<< _name << endl; cout <<"--- 调用基类 Print() ---"<< endl;Person::Print();}protected:int _age =666;// 派生类自己的年龄,隐藏基类 _ageint _stuid =2025101010;};intmain(){ Student s; cout <<"--- 调用派生类 Print() ---"<< endl; s.Print();// 调用 Student::Print cout <<"--- 直接调用基类 Print() ---"<< endl; s.Person::Print();// 调用基类 Printreturn0;}
在这里插入图片描述

2. 考察继承作用域选择题

classA{public:voidfun(){ cout <<"func()"<< endl;}};classB:publicA{public:voidfun(int i){ cout <<"func(int i)"<< i << endl;}};intmain(){ B b; b.fun(10); b.fun();// ⚠️ 注意这里return0;}
A 和 B 中两个 fun 的关系?A::fun() 和 B::fun(int),B 类中定义了一个与基类同名的函数 fun。在 C++ 中,如果派生类中定义了一个与基类同名的函数,会隐藏基类所有同名函数(不管参数列表是否相同)。注意:这不是重载(overload),因为重载是在同一个类中根据参数类型或个数区分函数。程序编译运行结果?b.fun(10); ✅ 可以正常调用 B::fun(int) 但是b.fun(); ❌因为编译器找不到匹配的函数因为基类 A::fun() 被 B::fun(int) 隐藏了。
classB:publicA{public:using A::fun;// 引入基类 fun() 到当前作用域voidfun(int i){ cout <<"func(int i)"<< i << endl;}};

此时就能够同时调用b.fun(10); b.fun();


四、派生类中的默认成员函数

在这里插入图片描述


6个默认成员函数,意思就是我们不写,编译器也会自动生成,那么他们分别是如何生成的呢?

派生类的构造函数在走初始化列表时会优先自动调用基类的默认构造函数完成基类那一部分成员的初始化,如果基类没有默认成员函数,那么在派生类的构造函数中必须显示调用基类的构造函数
// 基类classPerson{public:// 构造函数:如果没有传递 name 参数,则使用默认值 "peter"Person(const string& name ="peter"):_name(name)// 使用初始化列表初始化 _name{ cout <<"Person()"<< endl;// 输出提示,表示基类构造函数被调用}private: string _name;// 姓名属性,用于存储人的名字};// 派生类classStudent:publicPerson{public:// 构造函数:接受 name 和 id 参数来初始化对象Student(const string& name,int id):_id(id)// 初始化派生类的成员 _id{ cout <<"Student()"<< endl;// 输出提示,表示派生类构造函数被调用}private:int _id;// 学号属性,用于存储学生的学号};
  1. 基类: 这段代码具有一个带有默认参数的构造函数(如果没有传递 name 参数,则使用默认值 “peter” Person(const string& name = "peter");然后使用初始化列表来初始化成员变量_name,即_name会被赋予传入name参数值,如果没用传递参数,默认为peter。
  2. 派生类: 构造函数Student(const string& name, int id)会接受两个参数,name和id用来初始化对象,值得注意的是在这个构造函数里面,基类的构造函数没用显示调用,但是由于基类的构造函数有默认参数,编译器会自动调用基类构造函数来初始化基类的_name。
在这里插入图片描述
//基类classPerson{public:// 构造函数// 如果没有传递 name 参数,则默认使用 "peter"Person(const string& name):_name(name)// 使用初始化列表初始化 _name{ cout <<"Person()"<< endl;// 构造函数的输出,表示对象创建}private: string _name;// 姓名属性,存储人的名字};//派生类classStudent:publicPerson{public://构造函数Student(const string& name,int id):Person(name)//调用基类的构造函数初始化基类的那一部分成员,_id(id)//初始化派生类的成员{ cout <<"Student()"<< endl;}private:int _id;//学号};
  1. 基类: 这段代码的构造函数是没用默认参数的,要求我们再创建Person对象的时候必须提供一个name的参数,这个参数会在初始化列表赋值给_name。
  2. 派生类: 派生类中的构造函数同样需要接受两个参数,name和id用来初始化对象,因为基类中构造函数没有默认参数,我们必须显示调用基类构造函数,将name传递给Person的构造函数来初始化基类的_name。
在这里插入图片描述
派生类的拷贝构造函数必须显示调用基类的拷贝构造函数来完成基类的拷贝初始化。拷贝构造函数也属于构造函数,如果我们在进行派生类的拷贝构造的时候没有进行显式调用,编译器就会去调用基类的默认构造函数去完成对基类部分的初始化,这时候基类中的数据就不是我们想要进行拷贝的数据,而是初始化的数据。
classPerson{public:// 构造函数// 如果没有传递 name 参数,则默认使用 "peter"Person(const string& name):_name(name)// 使用初始化列表初始化 _name{ cout <<"Person()"<< endl;// 构造函数的输出,表示对象创建}//拷贝构造函数Person(const Person& p):_name(p._name){ cout <<"Person(const Person& p)"<< endl;}private: string _name;// 姓名属性,存储人的名字};classStudent:publicPerson{public://构造函数Student(const string& name,int id):Person(name),_id(id)//初始化派生类的成员{ cout <<"Student()"<< endl;}//拷贝构造函数Student(const Student& s):Person(s)//显示调用基类的拷贝构造函数完成基类成员的拷贝构造,_id(s._id)//拷贝构造派生类的成员{ cout <<"Student(const Student& s)"<< endl;}private:int _id;//学号};intmain(){ Student s("DaiTou",20231010); Student s1(s);return0;}
在这里插入图片描述


在这里插入图片描述
派生类的 赋值运算符(operator=) 需要显式地调用基类的赋值运算符,以确保基类部分的成员正确地被复制。如果你在派生类的 operator= 中不显示调用基类的 operator=,基类的 _name 就不会被正确赋值,导致 s2 的基类部分依然保持原来的值(可能是默认值或不正确的值)。值得注意的是派生类的赋值运算符隐藏了基类的赋值运算符,所以显示调用基类的operator=,需要指定基类作用域
//基类classPerson{public:// 构造函数// 如果没有传递 name 参数,则默认使用 "peter"Person(const string& name ="Peter"):_name(name)// 使用初始化列表初始化 _name{ cout <<"Person()"<< endl;// 构造函数的输出,表示对象创建}//拷贝构造函数Person(const Person& p):_name(p._name){ cout <<"Person(const Person& p)"<< endl;}//赋值运算符重载函数 Person&operator=(const Person& p){ cout <<"Person& operator=(const Person& p)"<< endl;if(this!=&p){ _name = p._name;}return*this;}private: string _name;// 姓名属性,存储人的名字};//派生类classStudent:publicPerson{public://构造函数Student(const string& name,int id):Person(name),_id(id)//初始化派生类的成员{ cout <<"Student()"<< endl;}//拷贝构造函数Student(const Student& s):Person(s)//显示调用基类的拷贝构造函数完成基类成员的拷贝构造,_id(s._id)//拷贝构造派生类的成员{ cout <<"Student(const Student& s)"<< endl;}//赋值运算符重载函数 Student&operator=(const Student& s){ cout <<"Student& operator=(const Student& s)"<< endl;if(this!=&s){ Person::operator=(s);//显示调用基类的operator=完成基类成员的赋值 _id = s._id;//完成派生类成员的赋值}return*this;}private:int _id;//学号};intmain(){ Student s("DaiTou",20231010); Student s1(s); Student s2("BuDai",20231212); s1 = s2;return0;}
在这里插入图片描述
派生类的析构函数会在被调用完之后自动调用基类的析构函数完成对基类成员的清理。因为派生类对象可能管理一些资源或成员,在基类析构之前,派生类需要完成自己的资源清理。如果先调用基类的析构函数,派生类的析构函数可能会尝试访问已经销毁的基类成员,这样会导致未定义的行为或错误。C++ 会自动处理析构函数的调用顺序,基类的析构函数不需要在派生类析构函数中显式调用。
//基类classPerson{public:// 构造函数// 如果没有传递 name 参数,则默认使用 "peter"Person(const string& name ="Peter"):_name(name)// 使用初始化列表初始化 _name{ cout <<"Person()"<< endl;// 构造函数的输出,表示对象创建}//拷贝构造函数Person(const Person& p):_name(p._name){ cout <<"Person(const Person& p)"<< endl;}//赋值运算符重载函数 Person&operator=(const Person& p){ cout <<"Person& operator=(const Person& p)"<< endl;if(this!=&p){ _name = p._name;}return*this;}//析构函数~Person(){ cout <<"~Person()"<< endl;}private: string _name;// 姓名属性,存储人的名字};//派生类classStudent:publicPerson{public://构造函数Student(const string& name,int id):Person(name),_id(id)//初始化派生类的成员{ cout <<"Student()"<< endl;}//拷贝构造函数Student(const Student& s):Person(s)//显示调用基类的拷贝构造函数完成基类成员的拷贝构造,_id(s._id)//拷贝构造派生类的成员{ cout <<"Student(const Student& s)"<< endl;}//赋值运算符重载函数 Student&operator=(const Student& s){ cout <<"Student& operator=(const Student& s)"<< endl;if(this!=&s){ Person::operator=(s);//显示调用基类的operator=完成基类成员的赋值 _id = s._id;//完成派生类成员的赋值}return*this;}//析构函数~Student(){ cout <<"~Student()"<< endl;//派生类的析构函数会在被调用完成后自动调用基类的析构函数}private:int _id;//学号};intmain(){ Student s("DaiTou",20231010); Student s1(s); Student s2("BuDai",20231212); s1 = s2;return0;}
在这里插入图片描述
派生类的析构函数会在被调用完成之后自动调用基类的析构函数这种方式有效的保证了在栈上先定义后析构,后定义先析构的顺序。定义派生类成员的时候,先调用派生类中基类的那一部分进行初始化,然后在生命周期结束后,由于派生类自己那部分的成员是后定义的,所以先调用派生类的析构函数进行析构,派生类中的在基类先定义那一部分就会调用基类的析构函数后析构。
因为多态中的一些析构函数会进行重写,重写的条件之一是函数名相同(放在多态中进行学习所以那么编译器会对析构函数的函数名进行重写,那么会统一修饰为destructor(),所以基类析构函数不加virtual的情况下,派生类的析构函数和基类析构函数构成隐藏)

五、 继承与友元

友元关系不能继承,也就是说明基类的友元可以访问基类的私有成员和保护成员,但是不能访问派生类的私有成员和保护成员。但是基类的友元函数想访问基类的派生类的私有和保护成员,应该声明为派生类的友元函数。
// 友元函数示例// 基类 PersonclassPerson{// 友元函数声明:Print 允许访问 Person 类的私有成员friendvoidPrint(const Person& p);// 友元函数 Print 被声明为 Person 类的友元函数public:protected:// 保护成员:年龄(_age),默认为 20int _age =20;private:// 私有成员:name 是私有的,只有友元函数或者该类内部可以访问// 私有成员无法直接通过外部对象访问 string _name ="DaiTou";};// 友元函数定义:该函数可以访问 Person 类的私有成员// 注意:Print 函数是一个全局函数,可以访问 Person 的私有成员(因为它是 Person 的友元)voidPrint(const Person& p){ cout << p._age << endl;// 访问 Person 类的 _age 成员 cout << p._name << endl;// 访问 Person 类的 _name 成员}// 派生类 StudentclassStudent:publicPerson{// 友元函数声明:Print 也可以访问 Student 类及其基类 Person 的私有成员friendvoidPrint(const Person& p);// 友元函数 Print 被声明为 Student 类的友元函数public:// 公共成员:学号(_stuid),默认为 20251010int _stuid =20251010;protected:// 保护成员:暂无};// 主函数:创建 Person 和 Student 类型的对象,调用 Print 函数intmain(){ Student s;// 创建一个 Student 类型的对象 Person p;// 创建一个 Person 类型的对象Print(p);// 调用 Print 函数打印 Person 类型对象 p 的成员Print(s);// 调用 Print 函数打印 Student 类型对象 s 的成员return0;}
在这里插入图片描述

六、继承与静态成员

基类定义了static的静态成员,则在整个继承体系中都只有一个这样的成员。无论派生出多少个派生类整个继承体系都只有一个static静态成员。当我们定义一个_count基类的静态成员变量的时候,我们让它在基类的构造函数和拷贝构造函数进行自增,这样我们就能够在该时刻实时获取已经实例化的基类和派生类的总个数(因为派生类在进行构造的时候会优先调用基类的默认构造完成派生类中在基类的那一部分成员的初始化)
#include<iostream>usingnamespace std;// 基类 PersonclassPerson{public:// 构造函数,每次创建一个 Person 对象时,_count 自增Person(){ _count++;}// 静态成员变量:计数器,用于统计创建了多少个 Person 或其派生类对象staticint _count;};// 静态成员变量的定义与初始化int Person::_count =0;// 该静态变量仅在 Person 类中定义和初始化// 派生类 StudentclassStudent:publicPerson{public:// 默认构造函数Student(){// 每当创建一个 Student 对象时,_count 会在基类 Person 中自增}};intmain(){ Person p;// 创建一个 Person 对象,_count 会自增至 1 Student s1;// 创建一个 Student 对象,_count 会自增至 2 Student s2;// 创建另一个 Student 对象,_count 会自增至 3 Student s3;// 再创建一个 Student 对象,_count 会自增至 4// 输出 _count,显示创建的 Person 和 Student 对象的总数 cout <<"Total number of objects created: "<< Person::_count << endl;return0;}
在这里插入图片描述

七、继承和组合

特性继承(is-a组合(has-a
关系类型is-a 关系,即派生类是基类的一种类型has-a 关系,即对象包含另一个对象
复用类型白箱复用:派生类可以访问基类的实现细节黑箱复用:对象细节不可见,只通过接口访问
耦合度高,基类变化会影响派生类低,改变一个对象不会影响另一个对象
优点代码复用,继承可以拓展基类功能类之间的关系松散,低耦合,易于维护
缺点破坏封装,依赖性强,基类变动可能影响派生类需要定义清晰的接口
使用场景类之间是 is-a 关系,且需要多态支持类之间是 has-a 关系,且不需要多态

总结

C++中的继承是面向对象编程的核心机制之一,它允许通过父类复用代码并扩展子类的功能。通过继承,子类可以继承父类的属性和方法,并根据需要进行扩展或重写。继承分为三种方式:public、protected和private继承,不同方式会影响访问权限。继承不仅可以带来代码复用,还能够实现多态和重写,使得程序更加灵活和可扩展。然而,继承也有其缺点,如破坏封装性、耦合度较高等,因此在实际设计时需要合理权衡,选择继承还是组合。


不是呆头将一直坚持用清晰易懂的图解+代码语言,让每个知识点变得简单!
👁️ 【关注】 看一个非典型程序员如何用野路子解决正经问题
👍 【点赞】 给“不写八股文”的技术分享一点鼓励
🔖 【收藏】 把这些“奇怪但有用”的代码技巧打包带走
💬 【评论】 来聊聊——你遇到过最“呆头”的 Bug 是啥?
🗳️ 【投票】 您的投票是支持我前行的动力
技术没有标准答案,让我们一起用最有趣的方式,写出最靠谱的代码! 🎮💻

Read more

生物细胞学在AI时代下的最新进展(2026版)

生物细胞学在AI时代下的最新进展(2026版)

从“看细胞”到“预测细胞”,人工智能正在怎样改写细胞生物学? 过去几年,人工智能在生命科学中最出圈的应用,往往集中在蛋白质结构预测、分子设计和药物筛选上。AlphaFold让人们第一次如此直观地感受到:原来一个看似极度复杂的生物问题,真的可能被大规模数据、模型架构和计算能力共同推进到“范式改变”的节点。可如果把视角从蛋白质拉回实验室,从分子层面的结构预测,回到细胞生物学研究者每天面对的培养箱、显微镜、图像、单细胞测序矩阵和反复调参的分析脚本,你会发现另一场同样深刻、却更贴近日常科研的变化,也已经开始发生。(Nature) 这场变化的核心,不只是“AI 让分析更快”。更准确地说,AI正在把细胞生物学中的许多传统环节,从“依赖人工经验、低通量、强主观”的工作方式,改造成“高维、可重复、可批量、可预测”的数据流程。过去,研究者常常用显微镜“看见”细胞;现在,越来越多的工作开始让模型去“读懂”细胞。

By Ne0inhk
除了 OpenClaw,今天 AI 热榜还有什么值得看?我把 5 个重点方向讲清楚了

除了 OpenClaw,今天 AI 热榜还有什么值得看?我把 5 个重点方向讲清楚了

🔥 个人主页:杨利杰YJlio❄️ 个人专栏:《Sysinternals实战教程》《Windows PowerShell 实战》《WINDOWS教程》《IOS教程》《微信助手》《锤子助手》《Python》《Kali Linux》《那些年未解决的Windows疑难杂症》🌟 让复杂的事情更简单,让重复的工作自动化 除了 OpenClaw,今天 AI 热榜还有什么值得看?我把 5 个重点方向讲清楚了 * 除了 OpenClaw,今天 AI 热榜还有什么值得看?我把 5 个重点方向讲清楚了 * 1. 我先说结论:今天这波 AI 热榜,最重要的不是“谁最火”,而是“风向变了” * 2. GoogleCloudPlatform / generative-ai:平台生态正在成为真正的护城河 * 3. MiroFish:群体智能和多智能体,开始从概念走向更具体的产品叙事

By Ne0inhk

Trae IDE 安装与使用保姆级教程:字节跳动的 AI 编程神器

一、Trae 是什么? Trae(发音 /treɪ/)是字节跳动推出的 AI 原生集成开发环境(AI IDE),于 2025 年 1 月正式发布。与传统的 IDE + AI 插件组合不同,Trae 从底层架构上就将 AI 能力深度集成,实现了真正意义上的"AI 主导开发"。 核心定位 Trae 以 “自主智能体(Agent)” 为核心定位,彻底重构了传统开发流程: * Chat 模式:智能代码补全、问答、解释和优化 * Builder 模式:自然语言一键生成完整项目框架 * SOLO 模式:AI 自主规划并执行开发任务 版本划分 版本定位核心特色适用人群Trae

By Ne0inhk
OpenClaw Mac本地部署保姆级教程:手把手教你“养龙虾”

OpenClaw Mac本地部署保姆级教程:手把手教你“养龙虾”

目录 一、部署前必读:你的Mac够格“养虾”吗? 1.1 硬件要求(别担心,要求不高) 1.2 你需要准备的东西 二、Step 1:安装Homebrew(Mac的“应用商店”) 三、Step 2:安装Node.js(OpenClaw的运行环境) 可选但推荐:安装Redis 四、Step 3:安装OpenClaw(核心步骤) 4.1 一键安装脚本(最简单,推荐) 4.2 如果一键脚本失败(备用方案:手动安装) 4.3 解决“command not found”问题

By Ne0inhk