Em C, você costumava / às vezes (por uma questão de estilo) usar uma static
variável de escopo de arquivo em que usaria uma variável de membro de classe privada em C ++. Ao dimensionar para programas multithread, basta adicionar o thread_local
C11 ou a extensão suportada por __thread
muito tempo. Eu sei que você pode fazer exatamente o mesmo em C como C ++, colocando tudo dentro de um struct
e fazendo um conjunto de funções que leva um ponteiro para isso struct
como seu primeiro argumento. Algumas bibliotecas fazem isso extensivamente. Mas meu estilo pessoal é manter o struct
mínimo possível, se necessário.
Costumo ler ou ouvir algumas pessoas argumentando que variáveis 'globais' são muito ruins. Sigo suas razões, e a maioria de seus argumentos parece estar relacionada a extern
variáveis globais em termos de C. O que eles dizem é certamente verdade. Às vezes, uso 1 ou 2 das extern
variáveis declaradas em todo o programa, para simplificar bastante as coisas e quando é fácil controlá-las, mas ir mais longe tornará o programa imprevisível.
E as static
variáveis? Eles ainda têm o mesmo problema que as variáveis globais 'reais'? Talvez eu nem precise fazer essa pergunta e continue se achar que estou fazendo o certo, mas hoje vi outro tipo de postagem 'variáveis globais são ruins' e finalmente cheguei aqui pensando que talvez isso seja certo lugar para esse tipo de pergunta. Qual é o seu pensamento?
Esta pergunta não é uma duplicata de presente , porque esta questão pergunta sobre extern
e static
variáveis não-locais, enquanto a outra questão é sobre o arquivo-escopo e bloco de escopo de static
variáveis.
Respostas:
Em um programa C de design bem definido, uma variável estática de arquivo é semelhante a um membro estático privado de uma classe:
Ele só pode ser acessado por funções nesse arquivo, semelhante a como uma variável de membro estática privada pode ser acessada apenas por funções na classe em que está definida.
Existe apenas uma cópia da variável.
Sua vida útil é a vida útil do programa.
Uma
extern
variável seria uma variável global verdadeira, como em qualquer idioma que as suporte.Uma
static
variável não global não é tão ruim quanto uma global; de fato, eles são necessários em alguns casos.O acesso é controlado através das funções que você escreve. Isso ajuda na integridade dos dados, incluindo verificação de limites e segurança de threads. (nota: isso não garante a segurança da linha, é apenas uma ferramenta para ajudar ao longo do caminho)
Os dados são encapsulados: somente esse arquivo pode acessá-lo. É o mais próximo que C pode chegar do encapsulamento, onde várias funções podem acessar uma variável estática.
Variáveis globais são ruins, não importa o quê. As variáveis de arquivo estático têm os benefícios de uma variável estática privada, mas nenhuma das desvantagens de uma variável global.
O único problema é diferente de uma variável estática privada verdadeira, como no C ++, outros arquivos podem declarar uma
extern
variável correspondente à declaração e você não pode impedir o acesso. Em outras palavras, você conta com o sistema de honra para evitar transformá-lo em uma variável global.fonte
O estado global, incluindo
extern
variáveis e nãoconst
static
variáveis no escopo do arquivo ou nas funções, freqüentemente pode ser uma solução fácil para um determinado problema, mas há três problemas:static
torna o código não testável , porque asstatic
variáveis tendem a ser dependências não substituíveis. Ou, em mais palavras OOP-y: você não está seguindo o Princípio da inversão de dependência. Eu vim para C e C ++ a partir de linguagens dinâmicas, como Perl, portanto, meu modelo de custo é inclinado para indicadores de despacho e função virtuais e assim por diante. Com os idiomas atuais, existe algum conflito entre testabilidade e boa arquitetura, mas acho que o pequeno incômodo de tornar suas dependências explícitas e permitir que elas sejam substituídas nos testes é visivelmente compensado pela facilidade de escrever testes e, assim, garantir que seu software esteja funcionando como esperado. Sem tornar seu código mais dinâmico, o único mecanismo disponível para injetar dependências para um teste é a compilação condicional.O estado global torna difícil raciocinar sobre correção , e isso leva a erros. Quanto mais bits e partes têm acesso a uma variável e podem modificá-la, mais fácil é perder o controle do que está acontecendo. Em vez disso: prefira a atribuição única de variáveis! Prefira
const
sempre que razoável! Prefira guardar variáveis por meio de getters e setters, onde você pode introduzir verificações de correção. Enquanto o estado éstatic
e nãoextern
, ainda é possívelpara manter a correção, mas é sempre melhor presumir que eu em uma semana não será tão inteligente quanto eu agora. Especialmente em C ++, podemos usar classes para modelar várias abstrações que tornam impossível o uso indevido de algo; portanto, tente utilizar o sistema de tipos em vez de sua inteligência - você tem coisas mais importantes para pensar.O estado global pode implicar que suas funções não sejam reentrantes ou que possam ser usadas apenas em um contexto por vez. Imagine um driver de banco de dados que possa gerenciar apenas uma conexão! Essa é uma restrição totalmente desnecessária. Na realidade, as limitações geralmente são mais sutis, como uma variável global usada para agregar resultados. Em vez disso, torne seu fluxo de dados explícito e passe tudo pelos parâmetros de função. Novamente, as classes C ++ podem tornar isso mais gerenciável.
Obviamente,
static const NAMED_CONSTANTS
estão bem. O usostatic
de funções internas é muito mais complicado: embora seja útil para constantes inicializadas lentamente, pode ser bastante testável. Um compromisso é separar o cálculo do valor inicial da variável estática, para que ambas as partes possam ser testadas separadamente.Em programas pequenos e independentes, tudo isso não importa, e você pode continuar usando o
static
estado para seu deleite do coração. Porém, ao passar em torno de 500 LOC ou se estiver escrevendo uma biblioteca reutilizável, você deve realmente começar a pensar em boa arquitetura e uma boa interface sem restrições desnecessárias.fonte
thread_local
e compiladores há muito tempo o suportam como uma extensão antes da padronização.static
variáveis.Não considero variáveis com escopo de arquivo tão ruins quanto variáveis globais. Afinal, todos os acessos a essas variáveis são limitados a um único arquivo de origem. Com essa restrição, as variáveis de escopo do arquivo são tão boas ou ruins quanto um membro de dados estáticos privados em C ++, e você não proíbe o uso deles, não é?
fonte
static
armazenamento, que existem exatamente uma vez, não para cada objeto. Essas variáveis não precisam de nenhuma instância da classe para serem referenciadas pelo código da classe, portanto, não há necessidade de passar uma referência / ponteiro para um singleton. É exatamente isso que as variáveis de escopo do arquivo também alcançam. A única diferença é que ostatic
membro tem escopo de classe enquanto astatic
variável "global" possui escopo de arquivo. Mas esses dois escopos são muito parecidos em extensão, o que é o meu ponto. Eu concordo que os diferentes significados destatic
são confusos, no entanto.Está tudo ligado ao escopo da variável (não constante, algo mutável) na minha opinião. É uma opinião que carece de alguma nuance, mas é um contador e apelo pragmático para retornar aos fundamentos mais básicos àqueles que dizem: "Isso é absolutamente mau!" apenas para tropeçar em questões semelhantes às associadas ao que criticam, como condições de corrida.
Imagine que você tem uma função de 50.000 linhas com todos os tipos de variáveis declaradas no topo e
goto
instruções para saltar por todo o lugar. Isso não é muito agradável com um escopo de variável tão monstruoso, e tentar raciocinar sobre a função, e o que está acontecendo com essas variáveis, será extremamente difícil. Nesse caso monstruoso, a distinção normal entre efeito colateral "externo" e "interno" perde muito de seu objetivo prático.Imagine que você tenha um programa simples de 80 linhas, basta escrever uma vez e iniciar uma variável global (com link interno e escopo de arquivo ou link externo, mas de qualquer forma o programa é pequeno). Isso não é tão ruim.
Imagine que você tem uma classe monstruosa em uma linguagem orientada a objetos que contém a lógica de todo o programa com milhares e milhares de linhas de código para sua implementação. Nesse caso, suas variáveis-membro são mais problemáticas que as globais no programa de 80 linhas acima.
Se você deseja raciocinar melhor e com mais confiança sobre seu código, a segurança do thread (ou a falta dele), torná-lo mais previsível, verifique se seus testes têm uma boa cobertura sem que todos os tipos de possíveis casos extremos sejam perdidos, etc. , ajuda a restringir o acesso a variáveis.
A estática do escopo do arquivo tenderá a ter um escopo mais restrito do que aqueles com vínculo externo, mas se o seu arquivo de origem tiver 100.000 linhas de código, isso ainda é muito amplo. Portanto, para a estática do escopo do arquivo, se você não puder evitá-las, eu tentaria manter o escopo restrito, não tornando o arquivo de origem que pode acessá-las gigantesco, pois nesse caso, reduzir o escopo é reduzir o tamanho e o escopo do design do arquivo de origem, em oposição à função (para variáveis locais, incluindo parâmetros), classe (para variáveis-membro) ou possivelmente módulo (para globais com ligação externa, mas acessível apenas dentro do módulo) ou até mesmo software inteiro (para globais com ligação externa acessível a todo o software).
fonte