¿C # te da "menos cuerda para ahorcarte" que C ++? [cerrado]

14

Joel Spolsky caracterizó a C ++ como "suficiente cuerda para ahorcarse" . En realidad, estaba resumiendo "C ++ efectivo" de Scott Meyers:

Es un libro que básicamente dice: C ++ es suficiente cuerda para ahorcarse, y luego un par de millas extra de cuerda, y luego un par de píldoras suicidas que se disfrazan de M&M ...

No tengo una copia del libro, pero hay indicios de que gran parte del libro se relaciona con problemas de gestión de la memoria que parecen ser discutibles en C # porque el tiempo de ejecución maneja esos problemas por usted.

Aquí están mis preguntas:

  1. ¿C # evita las trampas que se evitan en C ++ solo mediante una programación cuidadosa? Si es así, ¿en qué medida y cómo se evitan?
  2. ¿Hay nuevas dificultades diferentes en C # que un nuevo programador de C # debe tener en cuenta? Si es así, ¿por qué no podrían evitarse con el diseño de C #?
alx9r
fuente
10
Desde el FAQ : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. Creo que esto califica como tal pregunta ...
Oded
@Oded ¿Te estás refiriendo a la pregunta del titular de carácter limitado? ¿O mis más de 3 preguntas más precisas en el cuerpo de mi publicación?
alx9r
3
Francamente, tanto el título como cada una de las "preguntas más precisas".
Finalizado el
3
He comenzado una Meta discusión sobre esta pregunta.
Finalizado el
1
Con respecto a su tercera pregunta ahora eliminada, la serie C # efectiva de Bill Wagner (ahora 3 libros) me enseñó más sobre la programación de C # que cualquier otra cosa que leí sobre el tema. La revisión de Martins de EC # tiene razón en que nunca puede ser un reemplazo directo de C ++ efectivo, pero se equivoca al pensar que debería serlo. Una vez que ya no tenga que preocuparse por los errores fáciles , debe pasar a errores más difíciles .
Mark Booth

Respuestas:

33

La diferencia fundamental entre C ++ y C # proviene del comportamiento indefinido .

No tiene nada que ver con la gestión manual de la memoria. En ambos casos, ese es un problema resuelto.

C / C ++:

En C ++, cuando comete un error, el resultado no está definido.
O, si intenta hacer ciertos tipos de suposiciones sobre el sistema (por ejemplo, desbordamiento de entero con signo), es probable que su programa no esté definido.

Tal vez lea esta serie de 3 partes sobre comportamiento indefinido.

Esto es lo que hace que C ++ sea tan rápido: el compilador no tiene que preocuparse por lo que sucede cuando las cosas salen mal, por lo que puede evitar verificar la corrección.

C #, Java, etc.

En C #, tiene la garantía de que muchos errores explotarán en su cara como excepciones, y le garantizamos mucho más sobre el sistema subyacente.
Esa es una barrera fundamental para hacer que C # sea tan rápido como C ++, pero también es una barrera fundamental para hacer que C ++ sea seguro, y hace que C # sea más fácil de trabajar y depurar.

Todo lo demás es simplemente salsa.

usuario541686
fuente
Todas las cosas indefinidas están realmente definidas por la implementación, por lo que si desborda un entero sin firmar en Visual Studio, obtendrá una excepción si ha activado los indicadores del compilador correctos. Ahora sé que esto es de lo que estás hablando, pero no es un comportamiento indefinido , es solo que la gente no suele comprobarlo. (lo mismo con un comportamiento verdaderamente indefinido como operator ++, está bien definido por cada compilador). Se podría decir lo mismo con C #, solo hay 1 implementación, un montón de 'comportamiento indefinido' si se ejecuta en Mono, por ejemplo. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb
1
¿Está realmente definido o solo definido por lo que haga la implementación actual de .net en la versión actual de Windows? Incluso el comportamiento indefinido de c ++ está completamente definido si lo define como lo que haga g ++.
Martin Beckett
66
Desbordar enteros sin signo no es UB en absoluto. Está desbordando enteros firmados, eso es UB.
DeadMG
66
@gbjbaanb: Como dijo DeadMG, el desbordamiento de entero firmado no está definido. Es no definido por la implementación. Esas frases tienen significados específicos en el estándar C ++, y no son lo mismo. No cometas ese error.
user541686
1
@CharlesSalvia: Uh, ¿cómo exactamente "C ++ hace que sea más fácil aprovechar el caché de la CPU" que C #? ¿Y qué tipo de control le da C ++ sobre la memoria que no puede tener en C #?
user541686
12

¿C # evita las trampas que se evitan en C ++ solo mediante una programación cuidadosa? Si es así, ¿en qué medida y cómo se evitan?

La mayoría lo hace, algunas no. Y, por supuesto, hace algunos nuevos.

  1. Comportamiento indefinido : el mayor obstáculo con C ++ es que hay una gran cantidad de lenguaje indefinido. El compilador literalmente puede hacer explotar el universo cuando haces estas cosas, y estará bien. Naturalmente, esto es poco común, pero es bastante común que su programa funcione bien en una máquina y realmente no tiene una buena razón para no funcionar en otra. O peor, actuar sutilmente diferente. C # tiene algunos casos de comportamiento indefinido en su especificación, pero son raros y en áreas del lenguaje que se utilizan con poca frecuencia. C ++ tiene la posibilidad de encontrarse con un comportamiento indefinido cada vez que realiza una declaración.

  2. Fugas de memoria : esto es menos preocupante para C ++ moderno, pero para principiantes y durante aproximadamente la mitad de su vida útil, C ++ hizo que sea muy fácil perder memoria. C ++ efectivo vino alrededor de la evolución de las prácticas para eliminar esta preocupación. Dicho esto, C # todavía puede perder memoria. El caso más común con el que se topan las personas es la captura de eventos. Si tiene un objeto y coloca uno de sus métodos como un controlador para un evento, el propietario de ese evento debe tener GC para que el objeto muera. La mayoría de los principiantes no se dan cuenta de que el controlador de eventos cuenta como referencia. También hay problemas al no disponer de recursos desechables que pueden perder memoria, pero estos no son tan comunes como los punteros en C ++ pre-efectivo.

  3. Compilación : C ++ tiene un modelo de compilación retardado. Esto lleva a una serie de trucos para jugar bien con él, y mantener bajos los tiempos de compilación.

  4. Cadenas : Modern C ++ lo hace un poco mejor, pero char*es responsable del ~ 95% de todas las infracciones de seguridad antes del año 2000. Para los programadores experimentados, se centrarán std::string, pero aún es algo para evitar y un problema en las bibliotecas antiguas / peores . Y eso es rezar para que no necesites soporte Unicode.

Y realmente, esa es la punta del iceberg. El problema principal es que C ++ es un lenguaje muy pobre para principiantes. Es bastante inconsistente, y muchos de los viejos y muy malos escollos han sido resueltos cambiando los modismos. El problema es que los principiantes necesitan aprender las expresiones idiomáticas de algo como Effective C ++. C # elimina muchos de estos problemas por completo y hace que el resto sea menos preocupante hasta que avance en el camino del aprendizaje.

¿Hay nuevas dificultades diferentes en C # que un nuevo programador de C # debe tener en cuenta? Si es así, ¿por qué no podrían evitarse con el diseño de C #?

Mencioné el problema del evento "pérdida de memoria". Esto no es un problema de lenguaje, ya que el programador espera algo que el lenguaje no puede hacer.

Otra es que el finalizador para un objeto C # no está técnicamente garantizado para ser ejecutado por el tiempo de ejecución. Esto generalmente no importa, pero hace que algunas cosas se diseñen de manera diferente de lo que cabría esperar.

Otro problema que he visto a los programadores es la semántica de captura de funciones anónimas. Cuando captura una variable, captura la variable . Ejemplo:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

No hace lo que ingenuamente se piensa. Esto imprime10 10 veces.

Estoy seguro de que hay muchos otros que estoy olvidando, pero el problema principal es que son menos penetrantes.

Telastyn
fuente
44
Las pérdidas de memoria son cosa del pasado, y también lo es char*. Sin mencionar que todavía puede perder memoria en C # muy bien.
DeadMG
2
Llamar a las plantillas "pegado de cadena glorificado" es un poco demasiado. Las plantillas son realmente una de las mejores características de C ++.
Charles Salvia el
2
@CharlesSalvia Claro, son la característica realmente distintiva de C ++. Y sí, eso es quizás una simplificación excesiva para el impacto de la compilación. Pero sí afectan desproporcionadamente los tiempos de compilación y el tamaño de salida, especialmente si no tienes cuidado.
Telastyn el
2
@deadMG ciertamente, aunque diría que muchos de los trucos de metaprogramación de plantillas utilizados / necesarios en C ++ están ... mejor implementados a través de un mecanismo diferente.
Telastyn
2
@Telastyn, el objetivo de type_traits es obtener información de tipo en tiempo de compilación para que pueda usar esta información para hacer cosas como plantillas especializadas o sobrecargar funciones de formas específicas usandoenable_if
Charles Salvia
10

En mi opinión, los peligros de C ++ son algo exagerados.

El peligro esencial es este: mientras C # le permite realizar operaciones de puntero "inseguras" utilizando unsafe palabra clave, C ++ (que en su mayoría es un superconjunto de C) le permitirá usar punteros cuando lo desee. Además de los peligros habituales inherentes al uso de punteros (que son los mismos con C), como pérdidas de memoria, desbordamientos de búfer, punteros colgantes, etc., C ++ presenta nuevas formas para que arruines las cosas.

Esta "soga extra", por así decirlo, de la que Joel Spolsky estaba hablando , básicamente se reduce a una cosa: escribir clases que manejan internamente su propia memoria, también conocida como la " Regla de 3 " (que ahora se puede llamar la Regla de 4 o regla de 5 en C ++ 11). Esto significa que si alguna vez desea escribir una clase que administre sus propias asignaciones de memoria internamente, debe saber lo que está haciendo o su programa probablemente se bloqueará. Debe crear cuidadosamente un constructor, un constructor de copia, un destructor y un operador de asignación, que es sorprendentemente fácil de equivocar, lo que a menudo resulta en extraños bloqueos en el tiempo de ejecución.

SIN EMBARGO , en la programación real de C ++ todos los días, es muy raro escribir una clase que administre su propia memoria, por lo que es engañoso decir que los programadores de C ++ siempre deben ser "cuidadosos" para evitar estos escollos. Por lo general, solo harás algo más como:

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

Esta clase se parece bastante a lo que haría en Java o C #: no requiere una administración de memoria explícita (porque la clase de la biblioteca std::stringse encarga de todo eso automáticamente), y no se requiere nada de "Regla de 3" desde el valor predeterminado El constructor de copia y el operador de asignación están bien.

Solo cuando intentas hacer algo como:

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

En este caso, puede ser complicado para los principiantes obtener la asignación, el destructor y el constructor de copia correctos. Pero para la mayoría de los casos, no hay razón para hacer esto. C ++ hace que sea muy fácil evitar la gestión manual de la memoria el 99% del tiempo mediante el uso de clases de biblioteca como std::stringy std::vector.

Otro problema relacionado es la gestión manual de la memoria de una manera que no tiene en cuenta la posibilidad de que se produzca una excepción. Me gusta:

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

Si some_function_which_may_throw()en realidad no lanzar una excepción, uno se queda con una pérdida de memoria debido a que la memoria asignada para sque nunca se recuperó. Pero, de nuevo, en la práctica esto ya no es un problema por la misma razón por la que la "Regla de 3" ya no es realmente un problema. Es muy raro (y generalmente innecesario) administrar su propia memoria con punteros sin formato. Para evitar el problema anterior, todo lo que debe hacer es usar un std::stringo std::vector, y el destructor se invocará automáticamente durante el desenrollado de la pila después de que se haya lanzado la excepción.

Por lo tanto, un tema general aquí es que muchas características de C ++ que no se heredaron de C, como la inicialización / destrucción automática, los constructores de copias y las excepciones, obligan a un programador a tener mucho cuidado al hacer la gestión manual de la memoria en C ++. Pero, de nuevo, esto es solo un problema si tiene la intención de hacer una gestión manual de la memoria en primer lugar, lo que casi nunca es necesario cuando se tienen contenedores estándar y punteros inteligentes.

Por lo tanto, en mi opinión, aunque C ++ le proporciona una gran cantidad de soga adicional, casi nunca es necesario usarlo para ahorcarse, y las trampas de las que Joel hablaba son trivialmente fáciles de evitar en C ++ moderno.

Charles Salvia
fuente
Era la regla de tres en C ++ 03 y ahora es la regla de cuatro en C ++ 11.
DeadMG
1
Podría llamarlo "Regla de 5" para el constructor de copia, el constructor de movimiento, la asignación de copia, la asignación de movimiento y el destructor. Pero la semántica de movimiento no siempre es necesaria solo para la gestión adecuada de los recursos.
Charles Salvia el
No necesita mover y copiar la tarea por separado. Copiar y cambiar idioma puede hacer ambos operadores en uno.
DeadMG
2
Responde la pregunta Does C# avoid pitfalls that are avoided in C++ only by careful programming?. La respuesta es "en realidad no, porque es trivialmente fácil evitar las trampas de las que Joel hablaba en C ++ moderno"
Charles Salvia
1
OMI, mientras que los lenguajes de alto nivel como C # o Java le proporcionan administración de memoria y otras cosas que se supone que lo ayudarán , no siempre hace lo que se supone. Todavía termina cuidando el diseño de su código para no dejar ninguna pérdida de memoria (que no es exactamente lo que llamaría en C ++). Según mi experiencia, incluso es más fácil administrar la memoria en C ++ porque sabes que se llamarán a los destructores y, en la mayoría de los casos, harán la limpieza. Después de todo, C ++ tiene punteros inteligentes para casos en los que el diseño no permite una gestión eficiente de la memoria. C ++ es genial, pero no para tontos.
Pijusn
3

Realmente no estaría de acuerdo. Quizás menos dificultades que C ++ tal como existía en 1985.

¿C # evita las trampas que se evitan en C ++ solo mediante una programación cuidadosa? Si es así, ¿en qué medida y cómo se evitan?

Realmente no. Reglas como la Regla de tres han perdido importancia masiva en C ++ 11 gracias a unique_ptry shared_ptrsiendo estandarizadas. Usar las clases estándar de una manera vagamente sensata no es "codificación cuidadosa", es "codificación básica". Además, la proporción de la población de C ++ que sigue siendo lo suficientemente estúpida, desinformada o ambas para hacer cosas como el manejo manual de la memoria es mucho menor que antes. La realidad es que los profesores que desean demostrar reglas como esa tienen que pasar semanas tratando de encontrar ejemplos donde aún se aplican, porque las clases estándar cubren prácticamente todos los casos de uso imaginables. Muchas técnicas efectivas de C ++ han seguido el mismo camino: el camino del dodo. Muchos de los otros no son realmente tan específicos de C ++. Déjame ver. Saltando el primer elemento, los siguientes diez son:

  1. No codifique C ++ como si fuera C. Esto es realmente solo sentido común.
  2. Restrinja sus interfaces y use encapsulación. OOP
  3. Los escritores de códigos de inicialización de dos fases deben quemarse en la hoguera. OOP
  4. Sepa qué valor tiene la semántica. ¿Es esto realmente específico de C ++?
  5. Restrinja sus interfaces nuevamente, esta vez de una manera ligeramente diferente. OOP
  6. Destructores virtuales. Si. Este probablemente todavía sea válido, de alguna manera. finaly overridehe ayudado a cambiar este juego en particular para mejor. Haga su destructor overridey le garantiza un buen error de compilación si hereda de alguien que no hizo su destructor virtual. Haga su clase finaly no puede aparecer un matorral pobre y heredar de ella accidentalmente sin un destructor virtual.
  7. Suceden cosas malas si fallan las funciones de limpieza. Esto no es realmente específico para C ++: puede ver el mismo consejo para Java y C #, y, bueno, casi todos los lenguajes. Tener funciones de limpieza que pueden fallar es simplemente malo y no hay nada de C ++ o incluso POO sobre este elemento.
  8. Tenga en cuenta cómo el orden de los constructores influye en las funciones virtuales. Hilarantemente, en Java (actual o pasado) simplemente llamaría incorrectamente la función de la clase Derivada, que es incluso peor que el comportamiento de C ++. En cualquier caso, este problema no es específico de C ++.
  9. Las sobrecargas de los operadores deben comportarse como la gente espera. No muy específico Demonios, apenas es una sobrecarga específica del operador, lo mismo podría aplicarse a cualquier función; no le des un nombre y luego hagas que haga algo completamente poco intuitivo.
  10. Esto en realidad ahora se considera una mala práctica. Todos los operadores de asignación muy seguros de excepciones se ocupan de la autoasignación muy bien, y la autoasignación es efectivamente un error lógico del programa, y ​​verificar la autoasignación simplemente no vale el costo de rendimiento.

Obviamente no voy a revisar todos los elementos de Effective C ++, pero la mayoría de ellos simplemente están aplicando conceptos básicos a C ++. Encontraría el mismo consejo en cualquier lenguaje de operador sobrecargado orientado a objetos con tipo de valor. Los destructores virtuales son casi el único que es una trampa de C ++ y aún es válido, aunque, posiblemente, con la finalclase de C ++ 11, no es tan válido como lo fue. Recuerde que Effective C ++ se escribió cuando la idea de aplicar OOP, y las características específicas de C ++, todavía era muy nueva. Estos elementos apenas tratan sobre las trampas de C ++ y más sobre cómo hacer frente al cambio de C y cómo usar OOP correctamente.

Editar: Las trampas de C ++ no incluyen cosas como las trampas de malloc. Quiero decir, para uno, cada error que puede encontrar en el código C también puede encontrarlo en un código C # inseguro, por lo que no es particularmente relevante, y en segundo lugar, solo porque el Estándar lo define para la interoperación no significa que su uso se considere C ++ código. El estándar también lo define goto, pero si tuviera que escribir una pila gigante de espagueti con él, lo consideraría su problema, no el idioma. Hay una gran diferencia entre "codificación cuidadosa" y "Seguir modismos básicos del idioma".

¿Hay nuevas dificultades diferentes en C # que un nuevo programador de C # debe tener en cuenta? Si es así, ¿por qué no podrían evitarse con el diseño de C #?

usingapesta Realmente lo hace. Y no tengo idea de por qué no se hizo algo mejor. Además, Base[] = Derived[]y casi todos los usos de Object, que existe porque los diseñadores originales no notaron el éxito masivo de las plantillas en C ++, y decidieron que "Hagamos que todo herede de todo y perdamos toda la seguridad de nuestro tipo" fue la elección más inteligente . También creo que puedes encontrar algunas sorpresas desagradables en cosas como las condiciones de carrera con los delegados y otras cosas divertidas. Luego hay otras cosas generales, como cómo los genéricos apestan horriblemente en comparación con las plantillas, la colocación forzada realmente innecesaria de todo en una class, y esas cosas.

DeadMG
fuente
55
Sin embargo, una base de usuarios educada o nuevas construcciones realmente no están disminuyendo la cuerda. Son solo soluciones para que menos personas terminen colgadas. Aunque todo esto es un buen comentario sobre Effective C ++ y su contexto en la evolución del lenguaje.
Telastyn
2
No. Se trata de cómo un montón de elementos en Effective C ++ son conceptos que podrían aplicarse igualmente a cualquier lenguaje orientado a objetos con tipos de valores. Y educar a la base de usuarios para codificar C ++ real en lugar de C definitivamente está disminuyendo la cuerda que C ++ le brinda. Además, esperaría que las nuevas construcciones de lenguaje estén disminuyendo la cuerda. Se trata de cómo el hecho de que el Estándar C ++ defina mallocno significa que debas hacerlo, más que solo porque puedes gotoser una puta como una perra, significa que es una cuerda con la que puedes ahorcarte.
DeadMG
2
Usar las partes C de C ++ no es diferente a escribir todo su código unsafeen C #, lo cual es igual de malo. También podría enumerar todas las trampas de codificar C # como C, si lo desea.
DeadMG
@DeadMG: así que realmente la pregunta debería ser "un programador de C ++ tiene suficiente cuerda para ahorcarse mientras sea un programador de C"
gbjbaanb
"Además, la proporción de la población de C ++ que sigue siendo lo suficientemente estúpida, desinformada o ambas para hacer cosas como el manejo manual de la memoria es mucho menor que antes". Cita necesaria.
dan04
3

¿C # evita las trampas que se evitan en C ++ solo mediante una programación cuidadosa? Si es así, ¿en qué medida y cómo se evitan?

C # tiene las ventajas de:

  • Al no ser compatible con C, se evita tener una larga lista de características del lenguaje "malvadas" (por ejemplo, punteros en bruto) que son sintácticamente convenientes pero que ahora se consideran mal estilo.
  • Tener una semántica de referencia en lugar de una semántica de valor, lo que hace que al menos 10 de los elementos efectivos de C ++ sean discutibles (pero introduce nuevas trampas).
  • Tener menos comportamiento definido por la implementación que C ++.
    • En particular, en C ++ la codificación de caracteres de char, string, etc. es definido por la implementación. El cisma entre el enfoque de Windows para Unicode ( wchar_tpara UTF-16, charpara "páginas de códigos" obsoletas) y el enfoque * nix (UTF-8) causa grandes dificultades en el código multiplataforma. C #, OTOH, garantiza que a stringes UTF-16.

¿Hay nuevas dificultades diferentes en C # que un nuevo programador de C # debe tener en cuenta?

Si: IDisposable

¿Existe un libro equivalente a "Effective C ++" para C #?

Hay un libro llamado Effective C # que es similar en estructura a Effective C ++ .

dan04
fuente
0

No, C # (y Java) son menos seguros que C ++

C ++ es localmente verificable . Puedo inspeccionar una sola clase en C ++ y determinar que la clase no pierde memoria u otros recursos, suponiendo que todas las clases referenciadas sean correctas. En Java o C #, es necesario verificar cada clase referenciada para determinar si requiere algún tipo de finalización.

C ++:

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

C#:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++:

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

C#:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.
Kevin Cline
fuente
3
... es bastante trivial en los IDE modernos determinar si algo hereda de IDisposable. El principal problema es que necesita saber usar auto_ptr(o algunos de sus familiares). Esa es la cuerda proverbial.
Telastyn
2
@Telastyn no, el punto es que siempre usas un puntero inteligente, a menos que realmente sepas que no necesitas uno. En C #, la declaración de uso es como la cuerda a la que te refieres. (es decir, en C ++ que tiene que acordarse de usar un puntero inteligente, ¿por qué entonces es C # no es tan malo a pesar de que usted tiene que recordar siempre utilizar una declaración usando)
gbjbaanb
1
@gbjbaanb ¿Porque qué? ¿El 5% de la mayoría de las clases de C # son desechables? Y sabes que debes deshacerte de ellos si son desechables. En C ++, cada objeto es desechable. Y no sabe si su instancia particular necesita ser tratada. ¿Qué sucede con los punteros devueltos que no son de una fábrica? ¿Es tu responsabilidad limpiarlos? No debería ser, pero a veces lo es. Y de nuevo, solo porque siempre debe usar un puntero inteligente no significa que la opción no deje de existir. Especialmente para los principiantes, este es un obstáculo significativo.
Telastyn
2
@Telastyn: Saber usar auto_ptres tan simple como saber usar IEnumerableo saber usar interfaces, o no usar coma flotante para moneda o algo así. Es una aplicación básica de DRY. Nadie que conozca los conceptos básicos de cómo programar cometerá ese error. A diferencia using. El problema usinges que debe saber para cada clase si es desechable (y espero que nunca cambie), y si no es desechable, automáticamente prohíbe todas las clases derivadas que podrían ser desechables.
DeadMG
2
Kevin: Uh, tu respuesta no tiene sentido. No es culpa de C # que lo estés haciendo mal. Usted qué NO depende de finalizadores en código C # correctamente escrita . Si tiene un campo que tiene un Disposemétodo, debe implementarlo IDisposable(la forma 'adecuada'). Si su clase hace eso (que es el equivalente a implementar RAII para su clase en C ++), y usa using(que es como los punteros inteligentes en C ++), todo funciona perfectamente. El finalizador está destinado principalmente a prevenir accidentes: Disposees responsable de la corrección, y si no lo está utilizando, bueno, es su culpa, no de C #.
user541686
0

Sí, 100% sí, ya que creo que es imposible liberar memoria y usarla en C # (suponiendo que se administre y no entre en modo inseguro).

Pero si sabes programar en C ++, lo que no hace un número increíble de personas. Estás bastante bien. Al igual que las clases de Charles Salvia, realmente no manejan sus recuerdos, ya que todo se maneja en clases STL preexistentes. Raramente uso punteros. De hecho, fui a proyectos sin usar un solo puntero. (C ++ 11 hace esto más fácil).

En cuanto a cometer errores tipográficos, errores tontos, etc. (por ejemplo: if (i=0)bc, la clave se atascó cuando presionó == realmente rápido) el compilador se queja, lo cual es bueno, ya que mejora la calidad del código. Otro ejemplo es olvidar las breakdeclaraciones de cambio y no permitirle declarar variables estáticas en una función (que a veces no me gusta, pero es una buena idea).


fuente
44
Java y C # empeoraron el problema =/ ==al usar ==para la igualdad de referencia e introducir la .equalsigualdad de valor. El programador pobre ahora tiene que hacer un seguimiento de si una variable es 'doble' o 'Doble' y asegurarse de llamar a la variante correcta.
Kevin Cline
@kevincline +1, pero en C # structpuedes hacer lo ==que funciona increíblemente bien ya que la mayoría de las veces solo se tienen cadenas, ints y flotantes (es decir, solo miembros de estructura). En mi propio código, nunca tengo ese problema, excepto cuando quiero comparar matrices. Creo que nunca comparo los tipos de lista o no estructura (string, int, float, DateTime, KeyValuePair y muchos otros)
2
Python lo hizo bien al usar ==para igualdad de valor y ispara igualdad de referencia.
dan04
@ dan04 - ¿Cuántos tipos de igualdad crees que tiene C #? Vea la excelente charla sobre rayos de ACCU: algunos objetos son más iguales que otros
Mark Booth