本文转自:OOP基本概念对比:RUST与CPP
对象封装
一般来说,一个对象,具有如下性质:- 一个区别于其他对象的名字
- 若干个属性(数据,或字段)
- 若干个行为(函数,或方法)
在C++中,使用public和private关键字来暴露或者保护对象内部的实现
//C++
#include <iostream>
#include <string>
using namespace std;
//类定义
class Person
{
public:
Person(string&& name,int salary) : name(name), salary(salary) {};
public://公共字段
string name;//公共属性
void SayHi() { cout << name << ":hi" <<endl; };//公共方法
private://私有字段
int salary;//私有属性
void Sleep() { cout << "sleep"; };//私有方法
};
int main()
{
//在栈上创建对象
Person bobFromStack("Bob",233);
//在堆上创建对象
auto pAliceFromheap = new Person("Alice",234);
bobFromStack.SayHi();
pAliceFromheap->SayHi();
system("pause");
return 0;
}
在Rust中,所有名字默认是private的,只能在当前文件中访问,如果要将名字暴露给外部,需要使用pub关键字
路径用于引用模块树中的项 - Rust 程序设计语言 中文版 (rustwiki.org)
//Rust
pub struct Person {//导出结构体名
pub name:String,//可供其他模块访问的公共字段
salary:i32,//仅能在本文件中访问的私有字段
}
impl Person {//为结构体实现方法
pub fn SayHi(&self) {//可供其他模块访问的公共方法
println!("{}:Hi",self.name);
}
fn Sleep(&self){//仅能在本文件中访问的私有方法
println!("Sleep");
}
}
fn main() {
//在栈上创建对象
let bobFromStack = Person{name: String::from("Bob"),salary:233,};
//在堆上创建对象
let aliceFromHeap = Box::new(Person{name: String::from("Alice"),salary:234,});
bobFromStack.SayHi();
aliceFromHeap.SayHi();
}
继承
//C++
class Worker: public Person {
public:
string company;
};
//RUST没有继承,使用组合而非继承来复用代码
// https://rustwiki.org/zh-CN/book/ch17-01-what-is-oo.html
/* 近来继承作为一种语言设计的解决方案在很多语言中失宠了,因为其时常带有共享多于所需的代码的风险。子类不应总是共享其父类的所有特征,但是继承却始终如此。如此会使程序设计更为不灵活,并引入无意义的子类方法调用,或由于方法实际并不适用于子类而造成错误的可能性。某些语言还只允许子类继承一个父类,进一步限制了程序设计的灵活性。 */
多态
C++函数重载
重载是指不同参数的静态函数可以使用相同的函数名
int Add(int i1, int i2)
{
return i1 + i2;
}
int Add(int i1, int i2, int i3)
{
return i1 + i2 + i3;
}
C++虚函数重写
重写是指子类重新实现了父类中的虚函数
class Base{
//使用virtual关键字定义虚函数
virtual void fun(int a){cout<<"1"<<endl;}
//纯虚函数
virtual void fn()=0;
};
class Derive: Base{
//重写父类中的虚函数
void fun(int a){cout<<"2"<<endl;}
};
C++非虚函数重定义
重定义是指子类重新实现了父类中的非虚函数
class Base{
//非虚函数fun
void fun(int a){cout<<"1"<<endl;}
};
class Derive: Base{
//名字与父类相同,仍为fun
void fun(int a,int b){cout<<"2"<<endl;}
};
RUST为类型实现Trait
Trait抽象了类的接口,只要是实现了对应Trait的任意类型,都将拥有接口所描述的特性,类比C++中的某些概念,Trait中的方法可以被沿用(类似于继承)或者重新实现(类似于重定义)
另外,如果在Trait中只提供声明不提供实现,那么实现该Trait的类型必须提供该声明的定义(类似于必须将纯虚函数实例化)
RUST不提供重载,如果要支持不同参数的同名函数,考虑使用泛型、tuple、Trait之间的互相配合
trait:定义共享的行为 - Rust 程序设计语言 中文版 (rustwiki.org)
pub struct Person {//导出结构体名
pub name:String,//可供其他模块访问的公共字段
salary:i32,//仅能在本文件中访问的私有字段
}
impl Person {//为结构体实现方法
pub fn SayHi(&self) {//可供其他模块访问的公共方法
println!("{}:Hi",self.name);
}
fn Sleep(&self){//仅能在本文件中访问的私有方法
println!("Sleep");
}
}
struct Robot{
name:String,
}
pub trait SayHello {
//提供了默认实现的的行为
fn sayHello(&self){
println!("Default Hello")
}
//未提供默认实现的行为,必须实现该行为才能使用本特征
fn mustSaySometing(&self);
}
impl SayHello for Person{
//sayHello覆盖默认行为
fn sayHello(&self){
println!("{}:Hello",self.name)
}
//实现mustSaySometing
fn mustSaySometing(&self){println!("Wtf?")}
}
impl SayHello for Robot{
//sayHello沿用默认行为
//实现mustSaySometing
fn mustSaySometing(&self){println!("Calm down!")}
}
fn main() {
let bob = Person{name: String::from("Bob"),salary:233,};
let alice = Robot{name: String::from("alice"),};
bob.sayHello();//Bob:Hello
alice.sayHello();//Default Hello
bob.mustSaySometing();//Wtf?
alice.mustSaySometing();//Calm down!
}
统一接口调用
在C++中,如果想调用多个对象的某个同名方法,不需要针对每个对象都写一次调用,利用虚函数即可
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Person
{
public:
Person(string&& name,int salary) : name(name), salary(salary) {};
public:
string name;
virtual void SayHi() { cout << name << ":hi" <<endl; };
private:
int salary;
void Sleep() { cout << "sleep"; };
};
class Worker :public Person
{
public:
Worker(string&& name, int salary,string&& company) :Person(move(name),salary),company(company) {};
public:
string company;
void SayHi() { cout << company << " " << name << ":hi" << endl; };
};
int main()
{
vector<Person*> vec;
auto bob = new Person("Bob",233);
auto alice = new Worker("Alice", 233, "Bili");
vec.push_back(bob);
vec.push_back(alice);
for (auto& item:vec)
{//当父类指针指向子类对象时,针对虚函数会分别调用子类中的方法
item->SayHi();
}
system("pause");
return 0;
}
而在RUST中,想要统一调用某个特定名字的方法,需要用到Trait Object的概念
为使用不同类型的值而设计的 trait 对象 - Rust 程序设计语言 中文版 (rustwiki.org)
pub struct Person {//导出结构体名
pub name:String,//可供其他模块访问的公共字段
salary:i32,//仅能在本文件中访问的私有字段
}
impl Person {//为结构体实现方法
pub fn SayHi(&self) {//可供其他模块访问的公共方法
println!("{}:Hi",self.name);
}
fn Sleep(&self){//仅能在本文件中访问的私有方法
println!("Sleep");
}
}
struct Robot{
name:String,
}
pub trait SayHello {
//提供了默认实现的的行为
fn sayHello(&self){
println!("Default Hello")
}
//未提供默认实现的行为,必须实现该行为才能使用本特征
fn mustSaySometing(&self);
}
impl SayHello for Person{
//sayHello覆盖默认行为
fn sayHello(&self){
println!("{}:Hello",self.name)
}
//实现mustSaySometing
fn mustSaySometing(&self){println!("Wtf?")}
}
impl SayHello for Robot{
//sayHello沿用默认行为
//实现mustSaySometing
fn mustSaySometing(&self){println!("Calm down!")}
}
fn main() {
let bob = Person{name: String::from("Bob"),salary:233,};
let alice = Robot{name: String::from("alice"),};
//使用Trait Object来存储实现了相同Trait的不同类型
let items:Vec<Box<dyn SayHello>> = vec![
Box::new(Person{name: String::from("Bob"),salary:235,}),
Box::new(Robot{name: String::from("alice"),}),
];
//调用不同类型中的同一个方法
for item in items {
item.mustSaySometing();
}
}