C ++ foi rapidamente modernizando-se nos últimos anos. Começando com a introdução de C ++ 11, a linguagem fez um grande passo à frente, bem como as coisas se alteraram sob o capô. Para o usuário típico do Arduino, alguns disso é irrelevante, talvez muitos, no entanto, a linguagem ainda nos fornece algumas boas características que podemos aproveitar conforme nós programamos nossos microcontroladores.

O moderno C ++ nos permite compor código mais limpo, muito mais conciso, além de tornar o código que compõemos muito mais reutilizáveis. O cumprimento são alguns métodos que utilizam novos recursos do C ++ que não adicionam a sobrecarga de memória, diminuem a velocidade ou o tamanho do impulso, uma vez que todos são tratados pelo compilador. Utilizando essas características da linguagem que você não precisa mais se preocupar em especificar uma variável de 16 bits, chamando a função errada com null ou panterando seus construtores com as inicializações. Os antigos métodos ainda são oferecidos, assim como você ainda pode utilizá-los, no entanto, no extremamente menos, depois de ler isso, você ficará muito mais consciente dos recursos mais novos à medida que começamos a vê-los em código Arduino.

Quão enormes são seus inteiros?

C ++ 11 introduziu uma série de aliases inteiros de largura fixa que fornecerão o número de bits (em múltiplos de 8) que você deseja. Há versões assinadas e não assinadas. Se você utilizar INT16_T para uma variável, você entende, é 16 bits, independentemente do qual o Arduino está sendo direcionado.

1.
2.
3.
4.
int8_t / uint8_t – um tipo assinado / não assinado que é precisamente 8 bits de tamanho.
int16_t / uint16_t – um tipo assinado / não assinado que é precisamente de 16 bits de tamanho.
int32_t / uint32_t – um tipo assinado / não assinado que é precisamente 32 bits de tamanho.
int64_t / uint64_t – um tipo assinado / não assinado que é precisamente 64 bits de tamanho.

Estes são aliases dos tipos subjacentes, então no Arduino Uno, Int8_t é exatamente o mesmo tipo que um char, bem como UINT16 é o mesmo tamanho que um int não assinado. Uma nota, se você estiver editando um arquivo ‘.cpp’, você terá que #include , no entanto em um arquivo ‘.ino’, você não.

Namespaces anônimos

A ideia de um namespace anônimo existe por um tempo em C ++, no entanto, em causa com a introdução de C ++ 11. No C ++ 11, o namespace anônimo é agora o método favorecido para especificar uma variável, função ou classe que é acessível apenas nos dados atuais (ou seja, eles têm internos, em vez de ligação externa). É também um bom método para consolidar as coisas que apenas o requisito é acessado dentro do presente arquivo. Namespace anônimo são igualmente chamados de “namespaces sem nome” porque, como o nome sugere, eles são definidos sem um nome:

1.
2.
3.
namespace {

}

Namespaces anônimos Tire a localização de declarar coisas estáticas. Qualquer coisa que você possa ter declarado como estático ou const ou escrito como um #define agora pode colocar em um namespace anônimo, bem como ter exatamente o mesmo impacto – qualquer coisa definida no interior não pode ser acessada fora dos dados ‘.cpp’ que o namespace está em. No Arduino IDE, se você adicionar uma guia, bem como fornecer a nova guia um nome que não terminar em ‘.cpp’ ou ‘.h’, os dados são fornecidos uma extensão de ‘.ino . ‘Quando você clica no botão’ Verificar ‘, todos os dados’ .ino ‘são concatenados em um único arquivo’ .cpp ‘. Isso implica que tudo o que você declara como sendo estático ou Const ou em um namespace anônimo será oferecido em cada data ‘.ino’ porque, quando eles são concatenados, todos eles acabam exatamente no mesmo arquivo ‘.cpp’. Isso normalmente não é um problema para os pequenos programas Arduino, no entanto, é ótimo estar consciente disso.

Ok, então agora entendemos sobre as ligações interiores e externas, bem como exatamente como os dados são concatenados antes de serem compilados. Exatamente como isso nos ajuda em nosso código? Bem, agora entendemos exatamente como definir variáveis ​​para garantir que eles não vazam em áreas que não são esperados para ser.

Em vez de escrever:

1.
2.
3.
4.
5.
6.
// isso deve ser utilizado com moderação de qualquer maneira
#define some_var 1000.
// variáveis ​​estáticas declaradas em um dado são regionais para o arquivo
INT16_T estático = 0;
// Variáveis ​​Const declaradas em um dado são regionais para os dados também
const16_t numleds = 4;

Agora você pode escrever:

1.
2.
3.
4.
5.
6.
7.
8.
9.
namespace {
const16_t some_var = 1000; // agora é seguro
int16_t contagem = 0; // Nenhum requisito para utilizar estática
const16_t numleds = 0; // ainda declarou const.

classe esta classhasacommonname {

};
}

Tudo está contido no namespace anônimo no início do arquivo. O compilador não ficará confuso se houver mais um, conte ou numleds definido em um arquivo diferente. Bem como ao contrário de estáticas, as classes declaradas em um namespace anônimo também são regionais para os dados.

Automático para as pessoas

A palavra-chave do carro, adicionada no C ++ 11, permite definir uma variável sem entender seu tipo. Quando definido, embora, como outras variáveis, o tipo não pode ser alterado, assim como rvariáveis ​​de outine C ++. O compilador C ++ utiliza a dedução descobriu o tipo de variável.

1.
2.
3.
Auto I = 5; // eu é do tipo int
Auto J = 7,5F; // j é do tipo float
auto k = getresult (); // k é de qualquer tipo getresult () retorna

Na compra para você especificar que você quer uma referência, você pode fazer isso:

1.
2.
Auto & amp; Temp1 = MyValue;
AUTO AUTO & AMP; Temp2 = MyValue;

O tipo apropriado é deduzido para dicas também:

1.
2.
int myValue = 5;
auto myValuePtr = & amp; myValue; // myValuePtr é uma dica para um int.

Auto é uma taquigrafia fantástica para aqueles tipos particulares e desafiadores. Funciona fantástico para definir instâncias de iterador!

Usando usando

Houve alguns métodos para produzir aliases em C ++: o mais humilde (e perigoso) #defino, bem como o typedef menos prejudicial. O utilização do Typedef é favorecido a um #defino, no entanto, eles têm algumas questões. A primeira é a legibilidade. Pense no cumprimento do TypeDef:

1.
TypeDef Void (* FP) (int, const char *);

À primeira vista, particularmente se você não utilizar muito C ++, pode ser desafiador identificar que isso está produzindo um alias, FP, que é uma dica para uma função que retorna o espaço, bem como um int. Parâmetro de string. Agora, vamos ver o C ++ 11 Way:

1.
usando fp = vazio (*) (int, const char *);

Agora podemos ver que o FP é um alias para algo, assim como é um pouco mais simples identificar que é uma dica de função que é um alias de.

A segunda diferença é um pouco mais esotérica, pelo menos na programação Arduino. Usings pode ser modalmente enquanto os typedefs não podem.

Nullptr.

Em C, bem como C ++ 98, o NULL é realmente definido como 0. Para ser compatível com o Backwards, o compilador C ++ permitirá que você inicialize uma variável de ponta usando 0 ou NULL.

Quando você começa a utilizar automaticamente, você começará a ver o código assim:

1.
2.
3.
Resultado Automático = GetResult (…);

if (resultado == 0) {…}

Apenas de dar uma olhada nisso, você não pode dizer se o GetResult retorna uma dica ou um inteiro. No entanto, mesmo entre aqueles que ainda utilizam C, não numerosos inspecionarão se resultado == 0, eles vão inspecionar se resultado == null.

1.
2.
3.
Resultado Automático = GetResult (…);

if (resultado == null) {…}

Se, mais tarde, você modificou GetResult para retornar um int, não há erro do compilador – a declaração ainda é válida, embora ainda apareça como deveria ser um ponteiro.

C ++ 11 Modificações Isto introduzindo Nullptr, que é um tipo de ponta real. Agora, você digita:

1.
2.
3.
Resultado Automático = GetResult (…);

if (resultado == nullptr) {…}

Agora você entende que o GetResult só pode retornar um ponteiro. Além de alguém modifica-o em você, eles receberão um erro de compilação se eles também não modificaram a instrução IF IF em você. A introdução de nullptr também implica que é mais segura sobrecarregar uma técnica em um tipo integral, bem como um tipo de ponta. Por exemplo:

1.
2.
3.
4.
5.
setvalue vazio (int I); // (1)
setvalue vazio (widget * w); // (2)

Setvalue (5); // telefonemas 1
Setvalue (nulo); // da mesma forma que os telefonemas 1

Porque NULL não é um ponteiro, o segundo contacte-nos para setvalue telefonemas a versão que leva um parâmetro inteiro. Agora podemos telefonar para o segundo setValue corretamente utilizando nullptr:

1.
Setvalue (nullptr); // telefonemas 2

É por isso que normalmente é considerado prejudicial para sobrecarregar uma função ou técnica com base em um parâmetro inteiro, bem como uma dica (para qualquer coisa). É mais seguro agora, porém ainda desaprovado.

Inicialização padrão

Considerando o cumprimento da classe:

1.
2.
3.
4.
5.
6.
7.
8.
classe foo {
Foo (): Foostring (nullptr) {…}
Foo (const char * str): Foostring (nullptr) {…}
Foo (const foo & amp; outro): Foostring (nullptr) {…}

privado:
char * foostring;
};

Nós inicializamos toda a variável com nullptr, o que é bom. Se mais uma variável de membro for adicionada a esta classe, agora temos que adicionar três initializações muito mais aos construtores. Se a sua aula tiver várias variáveis, você precisa adicionar Inicializadores para todos eles em todos os construtores. O C ++ 11 fornece a opção de inicializar as variáveis ​​em linha com a declaração.

1.
2.
3.

privado:
char * foostring = nullptr;

Com C ++ 11, podemos especificar um valor preliminar padrão – ainda podemos substituir isso em cada construtor se a exigência de, mas, se não, não importa exatamente como inúmeros construtores que adicionamos, somente requisitos para definir o valor em um só lugar. Se nós separamos nossa classe para um ‘.h’ dados, bem como um arquivo ‘.cpp’, um benefício adicionado é que só temos que abrir os dados ‘.h’ para adicionar, bem como inicializar um variável.

Escopo seus enums.

Uma das coisas que C ++ tenta fazer é permitir que o programador encapsule as coisas para que, por exemplo, quando você nomear uma variável, você não está nomeando involuntariamente a sua variável exatamente a mesma como outra coisa com o mesmo nome exato. O C ++ fornece ferramentas para permitir que você faça isso, como namespaces, bem como classes. O Hally Enum, no entanto, vaza suas entradasinto the surrounding scope:

1.
2.
3.
4.
5.
6.
7.
enum Colour {
Branco,
azul,
amarelo
};
// Doesn’t compile.  There’s already something in this range with the name ‘white’
auto white = 5; 

The variable ‘white’ can’t be defined since ‘white’ is part of an enum, as well as that enum leaks it’s entrances into the surrounding scope. C++11 introduces scoped enums which enable a couple of things that C++98 didn’t allow. The first, as the name implies, is that the enums are completely scoped. The method to produce a scoped enum is with the utilize of the ‘class’ keyword:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
enum class Colour {
Branco,
azul,
amarelo
};

auto white = 5; // This now works.

Colour c = white; // Error, nothing in this range has been defined called white.
Colour c = Colour::white; // Correto.

By default, scoped enums have an underlying type: int, so whatever size an int is on your platform, that’s the size that sizeof will return for your enum. before C++11, unscoped enums likewise had an underlying type, however the compiler tried to be wise about it, so it would identify what the underlying type was as well as not tell us – it might enhance it for size as well as produce an underlying type that was the smallest that might in shape the number of entries. Or it might enhance for speed as well as produce a type that was the fastest for the platform. All this implies is that the compiler understood what type an enum was, however we didn’t, so we couldn’t ahead state the enum in a different file. Por exemplo,

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11
12.
13
file1.h:

enum Colour {
Branco,
azul,
amarelo
};

file2.h:

enum Colour; // Error, in this file, the compiler doesn’t understand what the type of Colour is.

void SomeFunction(Colour c);

The only method is to #include header1.h in header2.h. For little projects, this is fine, however in bigger projects, adding an entry to the Colour enum will imply a recompilation of anything that includes header1.h or header2.h. Scoped enums have a default size: int, so the compiler always understands what size they are. And, you can modification the size if you wish:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11
12.
13
file1.h:

enum class Colour: std::int8_t {
Branco,
azul,
amarelo
};

file2.h:

enum class Colour: std::int8_t;

void SomeFunction(Colour c);

Now any type of data that includes file2.h doesn’t necessarily have to be recompiled (file2.cpp will have to, since you’ll have to #include file1.h in it in purchase get it to compile properly.)

Para concluir

You do have option when moving ahead programming your microcontroller. There’s no reason to utilize any type of of these if you don’t want to. I’ve found, however, that they assist clean up my code as well as assist me catch bugs at compile time rather than at run time. numerous are just syntactic sugar, as well as you may not like the change. Some are just niceties that make working in a challenging language a bit nicer. What’s holding you back from trying them out?

Leave a Reply

Your email address will not be published. Required fields are marked *