Reaprendendo notas C ++ (oito) objetos e classes


Os recursos OOP mais importantes:

  • abstrato
  • Encapsulamento e ocultação de dados
  • Polimorfismo
  • herdar
  • Reutilização de código

Para implementar esses recursos e combiná-los, a melhoria mais importante feita pelo C ++ é fornecer classes. A maioria dos artigos anteriores falava sobre programação orientada a processos.Agora vamos apresentar oficialmente a programação orientada a objetos.

1. Abstração e classe

1.1 Classes em C ++

Use a palavra-chave class para indicar o design da classe e typename não pode ser usado aqui. A especificação da classe consiste em duas partes:

Declaração de classe: descreve a parte de dados na forma de membros de dados e descreve a interface pública na forma de funções de membro (métodos).
Definição de método de classe: descreve como implementar funções de membro de classe

Simplificando, a declaração da classe fornece o blueprint da classe e a definição do método fornece os detalhes. Em uma classe, as funções de membro podem ser definidas na classe ou podem ser representadas por protótipos .

(1) Controle de acesso

As palavras-chave private e public descrevem o controle de acesso aos membros da classe. Os objetos da classe podem acessar diretamente as partes públicas, mas só podem acessar membros privados dos objetos por meio de funções de membro públicas (ou funções de amigo). C ++ também fornece a palavra-chave protegida, que será introduzida na herança de classe.

A ocultação de dados não apenas impede o acesso direto aos dados, mas também libera os desenvolvedores (usuários da classe) de saber como os dados são representados.

(2) Controlar o acesso aos membros: público ou privado

Ocultar dados é um dos principais objetivos da OOP, portanto , os itens de dados geralmente são colocados na parte do membro privado e as funções de membro que compõem a interface da classe são colocadas na parte pública . Obviamente, você também pode colocar funções de membro na parte privada, portanto, só pode chamá-las por meio de métodos públicos e não pode chamar funções de membro privadas diretamente do programa.

O privado na declaração da classe pode ser omitido, porque este é o método de controle de acesso padrão para a classe.

class World
{
    
    
	float mass;
publicvoid tellall(void);
}

Para enfatizar o conceito de ocultação de dados, usamos explicitamente privado aqui.

(3) Classe e estrutura

A descrição da classe se parece muito com uma declaração de estrutura (todas têm funções de membro, tags públicas e privadas). Na verdade, C ++ estende a estrutura para ter as mesmas características da classe. O tipo de acesso padrão da estrutura é público e a classe é privada.

Os programadores C ++ geralmente usam classes para implementar descrições de classes e limitam a estrutura a objetos de dados puros.

1.2 Implementar funções de membro de classe

(1) A definição de função de membro

As funções de membro são muito semelhantes às funções regulares, mas têm duas características especiais.

  • Ao definir uma função de membro de classe, use o operador de análise (: :) para identificar a classe à qual a função pertence;
  • Os métodos de classe podem acessar componentes privados da classe.

Definição de classe:

void Stock::update(double price)
{
    
    ...}

(2) Método Inline

A definição da função de membro A função na declaração de classe torna-se automaticamente uma função embutida . As declarações de classe geralmente usam pequenas funções de membro como funções embutidas.

Se desejar, você pode definir funções de membro fora da declaração de classe e torná-las funções embutidas. Basta usar o qualificador embutido ao definir a função na seção de implementação da classe:

inline void Stock::set_tot()
{
    
    ...}

As regras especiais das funções sequenciais exigem que sejam definidas em cada arquivo que as utiliza, portanto, geralmente colocamos as definições sequenciais no arquivo de cabeçalho da classe de definição.

2. Construtor e destruidor de classe

Um dos objetivos do C ++ é fazer com que o uso de objetos de classe seja o mesmo que usar tipos padrão. Para inicializar os objetos de classe como o tipo int, precisamos acessar membros privados. Portanto, precisamos criar funções de membro apropriadas para converter com sucesso a inicialização do objeto. Por esse motivo, C ++ fornece um construtor de classe de função de membro especial, que é usado especificamente para construir novos objetos.

2.1 Declare e defina o construtor

O protótipo do construtor é o seguinte, o protótipo a seguir usa parâmetros padrão.

Stock(const string& co, long n = 0, double pr = 0.0);

É definido da seguinte forma:

Stock::Stock(const string& co, long  n, double pr)
{
    
    
	company = co;//赋值给私有数据成员
	...
}

Para distinguir membros de dados privados de outras variáveis, sublinhados são frequentemente adicionados, como empresa_.

2.2 Usando o construtor

  • Mostrar construtor de chamada
Stock food = Stock("world cabbage", 250, 1.25);
  • Hermit chama o construtor
Stock garment("Furry Mason", 50, 2.5);

Cada vez que você cria um objeto de classe (até mesmo aloca memória dinamicamente usando new), C ++ usa o construtor de classe.

Stock * pstock = new Stock("Electroshock", 18, 19.0);

O objeto criado acima não tem nome, mas o endereço do objeto criado é atribuído a pstock.

  • Inicialização da lista C ++
Stock hot_tip = {
    
    "Derivatives", 100, 45};
Stock jock {
    
    "Sport Age Storage"};
Stock temp {
    
      };

2.3 Construtor padrão

Se nenhum valor inicial for fornecido, o construtor padrão será chamado. Por exemplo:

Stock fluffy_the_cat;//使用默认构造函数

Se nenhum construtor for fornecido na classe, C ++ fornecerá automaticamente um construtor padrão e não fará nenhum trabalho:

Stock::Stock( ) {
    
     };

É importante notar que o compilador fornecerá um construtor padrão se e somente se nenhum construtor for definido. Se a classe define um construtor, o usuário deve fornecer um construtor padrão para ele. Se um construtor não padrão for fornecido, mas nenhum construtor padrão for fornecido, a declaração a seguir estará errada.

Stock stock1;//没有默认构造函数,报错!!!

Observe a diferença entre as seguintes definições:

Stock first("Concrete");//隐式构造函数调用,非默认构造函数调用
Stock second();//定义一个函数,它的返回值为Stock类型
Stock third;//隐式地调用默认构造函数。

2.4 Destruidor

Quando o objeto expira, o programa chama automaticamente um destruidor de função de membro especial. O destruidor conclui o trabalho de limpeza, que na verdade é muito útil. Por exemplo, para limpar a memória alocada por new, a definição do destruidor só precisa adicionar ~ antes do nome da classe, então o destruidor de Stock é ~ Stock () .

Uma vez que o destruidor não realiza nenhum trabalho importante, ele pode ser escrito como uma função que não faz nada:

Stock::~Stock()
{
    
    ...}
  • Se você criar um objeto de classe de armazenamento estático, seu destruidor será chamado automaticamente no final do programa.
  • Se você criar um objeto de classe de armazenamento automático, seu destruidor será chamado automaticamente quando o programa terminar de executar o bloco de código.
  • Se o objeto for criado por new, ele residirá na área de armazenamento livre, e quando delete for usado para liberar a memória, seu destruidor será automaticamente chamado.

O destruidor é necessário.Se o programador não fornecer um destruidor, o compilador declarará hermeticamente um destruidor padrão.

2,5 função membro const

Se você definir um objeto const, ele não poderá chamar sua própria função. Por exemplo:

const Stock land = Stock("kludgehorn");
land.show();//不被允许,因为不能确保调用的对象不被修改!!!

A solução é adicionar const após a declaração da função-membro e a definição da função-membro para garantir que a função não modifique o objeto de chamada (não modifique o terreno) :

void show() const;//成员函数原型
void Stock::show() const//成员函数定义
{
    
    
	...
}

As funções-membro definidas dessa maneira são chamadas de funções-membro const.

3. este ponteiro

Às vezes, quando um método é projetado para dois objetos, é necessário usar o ponteiro this do C ++. Por exemplo, se quisermos comparar os valores de dois objetos, podemos chamá-lo como a seguinte chamada, não importa qual seja usada.

top = stock1.topval(stock2);//显示调用stock2,隐式调用stock1
top = stock2.topval(stock1);//显示调用stock1,隐式调用stock2

Fragmento de definição de código específico:

const Stock& Stock::topval(const Stock& s) const
{
    
    
	if(s.total_val > total_val)
		return s;//返回被调用的对象
	else
		return *this;//返回自己的对象本身,this是本身的地址
}

Observe os seguintes pontos no snippet de código acima:

  • s indica que o objeto é acessado explicitamente e o objeto que chama topval é acessado implicitamente.
  • O const entre colchetes significa que o objeto que é acessado explicitamente não será modificado, e const após os colchetes significa que o objeto acessado implicitamente não será modificado.
  • Este é o endereço do objeto acessado implicitamente e * isto representa o objeto.

4. Matriz de objetos

Podemos criar vários objetos em uma série de objetos. O seguinte cria 4 objetos, cada um dos quais chama o construtor padrão.

Stock mystuff[4];//创建4个对象

Podemos usar para acessar membros de dados e funções de membro:

mystuff[0].update();
mystuff[0].show();

Se não quisermos usar o construtor padrão, podemos escolher nós mesmos o tipo de construtor.

const int STKS = 4;
Stock stocks[STKS] = {
    
    
	Stock("Nano", 12.5, 20),
	Stock();
	Stock("Mono", 130, 3.25),
	};

stocks [0] e stocks [2] usam o mesmo construtor, stocks [1] usa o construtor padrão, stocks [3] não executa a inicialização do display, ele também chama o construtor padrão.

5. Escopo da classe

O escopo do nome definido na classe (como o nome do membro de dados da classe e o nome da função de membro da classe) é a classe inteira, e o escopo é que o nome de toda a classe só é conhecido na classe , mas não fora da classe.

Ao usar membros fora de uma classe, você deve usar o operador de membro direto (.), O operador de membro indireto (->) ou o operador de resolução de escopo (: :) dependendo do contexto.

5.1 Constantes cujo escopo é uma classe

Se o valor literal da declaração da classe for o mesmo para todos os objetos, podemos usar a classe para acessá-la. Mas a operação a seguir está errada, porque nenhum espaço é alocado quando a classe é definida .

class Bakery
{
    
    
	private:
		const int Months = 12;//不可以
		double consts[Months];

Temos duas maneiras de realizar as operações acima:

(1) Enumeração

class Bakery
{
    
    
	private:
		enum{
    
    Months = 12};
		double consts[Months];

Declarar enumerações dessa maneira não cria membros de dados de classe. Em outras palavras, todos os objetos não contêm enumerações. Além disso, Meses é apenas um nome simbólico. Quando o encontra no código de toda a classe, o compilador o substitui por 30.

(2) estático

class Bakery
{
    
    
	private:
		static const int Months = 12;
		double consts[Months];

Isso criará uma constante chamada Meses, que será armazenada com outras variáveis ​​estáticas em vez de no objeto. Portanto, há apenas uma constante de Meses, que é compartilhada pelo objeto Padaria . No C ++ 98, essa técnica só pode ser usada para declarar constantes estáticas com valores e inteiros ou enumerações, mas não pode armazenar constantes duplas. O C ++ 11 elimina essa limitação.

Observe que a inicialização na classe pode ser apenas membros de dados estáticos são do tipo integral ou enumerado const. Outros tipos precisam ser inicializados no arquivo cpp que define a classe.

5.2 Enumeração no escopo (C ++)

Existem alguns problemas com as enumerações tradicionais. Por exemplo, as enumerações a seguir entrarão em conflito.

enum egg {
    
    Small, Medium, Large, Jumbo };
enum t_shirt {
    
    Small, Medium, Large, Xlarge};

O código acima não pode ser compilado porque egg e t_shirt têm a mesma enumeração e eles entrarão em conflito. Para resolver este problema, C ++ 11 fornece uma nova enumeração cujo escopo é uma classe.

enum class egg {
    
    Small, Medium, Large, Jumbo };
enum class t_shirt {
    
    Small, Medium, Large, Xlarge};

Você também pode usar struct em vez de class; não importa qual método você use, você precisa usar o nome da enumeração para limitar a enumeração.

egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;

C ++ 11 também melhora a segurança de tipo de enumerações dentro do escopo, que não podem ser convertidas implicitamente em inteiros.

enum egg {
    
    Small, Medium, Large, Jumbo };//普通类型
enum class t_shirt {
    
    Small, Medium, Large, Xlarge};//类作用域
egg one = Medium;
t_shirt rolf = t_shirt::Large;
int king = one;//可以,普通类型转换
int ring = rolf;//不可以,不能进行作用域的转换
if(king < Jumbo)//可以
if(king < t_shirt::Medium)//不可以,不能隐士转换为int比较

Mas podemos converter explicitamente:

int Frodo = int(t_shirt::Small);

Por padrão, a implementação subjacente da enumeração é int, mas no C ++ 11, podemos usar explicitamente a implementação subjacente de um tipo específico, e o tipo subjacente deve ser um inteiro.

enum class : short t_shirt {
    
    Small, Medium, Large, Xlarge};//底层类型指定为short

6. Tipos de dados abstratos

Tipo de dados abstratos (tipo de dados abstratos, ADT), usando classes para representar conceitos gerais. As classes são muito adequadas para representar o ADT, as interfaces de função de membro público fornecem serviços descritos pelo ADT e as partes privadas das classes e os códigos de método de classe fornecem implementações , que são ocultadas dos clientes da classe.


Catálogo de Visão Geral
Anterior: (7) Modelo de Memória e Namespace
Próximo: (8) Classe de Uso


Referência do artigo: "C ++ Primer Plus Sixth Edition"

Acho que você gosta

Origin blog.csdn.net/QLeelq/article/details/111059334
Recomendado
Clasificación