这里为 c++++98 程序员全面演示了 python 中的 oop 概念:
类定义和对象创建
python
# privado por convenção: _underscore_simples # "realmente privado": __underscore_duplo (name mangling) # público: sem underscore from abc import abstractmethod class animal(abc): # em python, variáveis declaradas no escopo da classe e não dentro de um # método específico, são automaticamente compartilhadas por todas instâncias. species_count = 0 # além disso, elas podem ser inicializadas diretamente dentro da classe. # construtor def __init__(self, name): # variáveis de instância self.name = name # público self._age = 0 # protegido por convenção self.__id = id(self) # privado (mas você consegue acessar com name mangling) animal.species_count += 1 # destrutor def __del__(self): animal.species_count -= 1 # método regular @abstractmethod def make_sound(self): pass # equivalente a um método abstrato/virtual (deve ser implementado apenas nas classes filhas) # método estático (não precisa da instância para ser utilizado, nem utiliza seus atributos) @staticmethod def get_kingdom(): return "animalia" # método de classe (recebe a classe como primeiro argumento, pode acessar atributos da classe) @classmethod def get_species_count(cls): return cls.species_count # decorador de propriedade (getter) @property def age(self): return self._age # decorador de propriedade (setter) @age.setter def age(self, value): if value >= 0: self._age = value # métodos especiais (sobrecarga de operadores) def __str__(self): # como tostring() - para string legível return f"animal named {self.name}" def __repr__(self): # para debugging return f"animal(name='{self.name}')" def __eq__(self, other): # operador de comparação == return isinstance(other, animal) and self.name == other.name def __len__(self): # função len() return self._age def __getitem__(self, key): # operador de acesso [] if key == 'name': return self.name raise keyerror(key)
c++98
#include <iostream> #include <string> #include <sstream> class animal { public: static int species_count; animal(const std::string& name) : name(name), _age(0), __id(++id_counter) { // construtor ++species_count; } ~animal() { // destrutor --species_count; } virtual void make_sound() = 0; // método não implementável na classe base (virtual/abstrato) static std::string get_kingdom() { // não existe distinção entre // @classmethod e @staticmethod em cpp, apenas static methods. return "animalia"; } // static methods podem ser utilizados sem instanciar uma classe e têm // acesso às propriedades estáticas da classe: static int get_species_count() { return species_count; } // getter: int get_age() const { return _age; } // setter: void set_age(int age) { if (age >= 0) { _age = age; } } // implementação dos métodos especiais que vimos em python: std::string to_string() const { return "animal named " + name; } std::string repr() const { std::ostringstream oss; oss << "animal(name='" << name << "', age=" << _age << ", id=" << __id << ")"; return oss.str(); } bool operator==(const animal& other) const { return name == other.name; } // sobrecarga do operador [] std::string operator[](const std::string& key) const { if (key == "name") { return name; } throw std::out_of_range("invalid key"); } // método isinstance template <typename t> bool isinstance() const { return dynamic_cast<const t*>(this) != nullptr; } protected: std::string name; int _age; private: int __id; static int id_counter; }; // variáveis estáticas de classe são compartilhadas por todas as instâncias mas // precisam ser inicializadas separadamente. int animal::species_count = 0; int animal::id_counter = 0;
遗产
python
class dog(animal): def __init__(self, name, breed): # chama o construtor da classe pai super().__init__(name) self.breed = breed # sobrescreve o método da classe pai def make_sound(self): return "woof!"
c++98
class dog : public animal { public: dog(const std::string& name, const std::string& breed) : animal(name), breed(breed) {} void make_sound() override { std::cout << "woof!" << std::endl; } private: std::string breed; };
多重继承
python
class pet: def is_vaccinated(self): return true class domesticdog(dog, pet): pass
c++98
class pet { public: bool is_vaccinated() const { return true; } }; class domesticdog : public dog, public pet { public: domesticdog(const std::string& name, const std::string& breed) : dog(name, breed) {} };
抽象类
python
from abc import abc, abstractmethod class shape(abc): @abstractmethod def area(self): pass
c++98
class shape { public: virtual ~shape() {} virtual double area() const = 0; };
使用示例
python
if __name__ == "__main__": # cria objetos dog = dog("rex", "golden retriever") # acessa atributos print(dog.name) # público print(dog._age) # protegido (ainda acessível) # print(dog.__id) # isso falhará print(dog._animal__id) # isso funciona (acessando attribute privado com name mangling) # propriedades dog.age = 5 # usa setter automaticamente print(dog.age) # usa getter automaticamente # métodos estáticos e de classe print(animal.get_kingdom()) print(animal.get_species_count()) # verifica herança print(isinstance(dog, animal)) # true print(issubclass(dog, animal)) # true # métodos especiais em ação print(str(dog)) # usa __str__ print(repr(dog)) # usa __repr__ print(len(dog)) # usa __len__ print(dog['name']) # usa __getitem__
c++98
int main() { // cria objetos dog dog("rex", "golden retriever"); // acessa atributos std::cout << dog.name << std::endl; // público std::cout << dog.get_age() << std::endl; // protegido (ainda acessível) // std::cout << dog.__id << std::endl; // isso falhará (privado) // propriedades dog.set_age(5); // usa setter std::cout << dog.get_age() << std::endl; // usa getter // métodos estáticos e de classe std::cout << animal::get_kingdom() << std::endl; std::cout << animal::get_species_count() << std::endl; // equivalente aos "métodos especiais": // verifica herança if (dog.isinstance<animal>()) { std::cout << "dog é uma instância de animal" << std::endl; } std::cout << dog.to_string() << std::endl; // usa to_string std::cout << dog.repr() << std::endl; // usa repr std::cout << dog["name"] << std::endl; // usa operador [] }
python 和 c++98 之间的主要区别
- 没有公共/私有/受保护的关键字(使用命名约定)
- 多重继承不同:
- python 使用方法解析顺序 (mro) 和 c3 线性化
- 不需要像c++那样的虚拟继承
- super() 自动遵循 mro
- python 中基类的顺序很重要
- 您可以使用 __mro__ 检查解析顺序
- 默认情况下所有方法都是虚拟的
- 指针/引用之间没有区别
- 不需要内存管理(垃圾收集器)
- 动态类型而不是静态类型
- 属性装饰器而不是 getter/setter 方法
- 特殊方法使用 __name__ 格式而不是运算符
- 更多用于运算符重载的 pythonic 语法(例如 __eq__ 与运算符 ==)
关键字
使用 dir(Object) 查看对象的所有属性和方法,使用 help(object) 查看文档。
专题:
钻石继承问题
animal . ' , _______ _ .`_|___|_`. _ pet / / workinganimal ' ' / " / ./ domesticdog
c++98 中的 diamond 继承问题
当一个类继承自两个类,而这两个类又继承自一个公共基类时,就会发生钻石继承。这可能会导致几个问题:
- 歧义:公共基类的方法和属性可能会变得不明确。
- 数据重复:每个派生类都可以拥有自己的公共基类成员副本,从而导致数据重复。
c++98 中的 diamond 继承示例
class animal { public: animal() { std::cout << "animal constructor" << std::endl; } virtual void make_sound() { std::cout << "some generic animal sound" << std::endl; } }; class pet : public animal { public: pet() : animal() { std::cout << "pet constructor" << std::endl; } void make_sound() override { std::cout << "pet sound" << std::endl; } }; class workinganimal : public animal { public: workinganimal() : animal() { std::cout << "workinganimal constructor" << std::endl; } void make_sound() override { std::cout << "working animal sound" << std::endl; } }; class domesticdog : public pet, public workinganimal { public: domesticdog() : animal(), pet(), workinganimal() { std::cout << "domesticdog constructor" << std::endl; } void make_sound() override { pet::make_sound(); // ou workinganimal::make_sound(), dependendo do comportamento desejado } }; int main() { domesticdog dog; dog.make_sound(); return 0; }
预期行为
animal constructor pet constructor workinganimal constructor domesticdog constructor pet sound
在这个例子中,domesticdog继承自pet和workinganimal,它们都继承自animal。这创造了一颗传家宝钻石。使用虚拟继承来避免数据重复和歧义。
python 如何自动阻止 diamond 继承
python 使用方法解析顺序 (mro) 和 c3 线性化来自动解决菱形继承问题。 mro 确定在查找方法或属性时检查类的顺序。
python 中的 diamond 继承示例
class animal: def make_sound(self): print("some generic animal sound") class pet(animal): def make_sound(self): print("pet sound") class workinganimal(animal): def make_sound(self): print("working animal sound") class domesticdog(pet, workinganimal): pass dog = domesticdog() dog.make_sound()
预期行为
pet sound
在此示例中,python 使用 mro 自动解析菱形继承。您可以使用 __mro__:
属性检查 mro
print(domesticdog.__mro__)
python中的mro确保domesticdog正确继承自pet和workinganimal,并且animal在对象之前被解析。因此,声明顺序会影响 mro,但 c3 线性化可确保尊重层次结构。
解释:
- 声明顺序:mro 从最派生的类开始,遵循基类声明的顺序。
- c3 线性化:确保每个类出现在其超类之前,并保持继承顺序。
数据结构:栈、队列和映射
堆
python
stack = [] # we could just use a list as a stack stack.append(1) # push stack.append(2) print(stack.pop()) # pop
c++98
#include <stack> // we have to import the stack type std::stack<int> stack; stack.push(1); // push stack.push(2); std::cout << stack.top() << std::endl; // top stack.pop(); // pop
队列
python
from collections import deque queue = deque() queue.append(1) # enqueue queue.append(2) print(queue.popleft()) # dequeue
c++98
#include <queue> std::queue<int> queue; queue.push(1); // enqueue queue.push(2); std::cout << queue.front() << std::endl; // front queue.pop(); // dequeue
地图
python
map = {} # this is automatically creating a map (which is called a dictionary in python) map['key1'] = 'value1' map['key2'] = 'value2' print(map['key1'])
c++98
#include <map> std::map<std::string, std::string> map; map["key1"] = "value1"; map["key2"] = "value2"; std::cout << map["key1"] << std::endl;
感谢您遵循本有关 python 和 c++98 中的 oop 概念的指南。我们希望它对您的学习之旅有用。如果您喜欢内容,请留下您的评论、点赞并分享给您的朋友和同事。如果您发现错误,请留下您的评论,我会纠正它!下次见!