Die Idee der Klasse mit ihren "domestizierten" Funktionen ist hervorragend. Da hat C nur die Struktur mit "freien" Funktionen zu bieten. Das bezahlt man in C++ mit viel Klassenbeziehungs-Wirrwarr und im kernel nutzlosen Overhead.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Die Idee der Klasse mit ihren "domestizierten" Funktionen ist hervorragend. Da hat C nur die Struktur mit "freien" Funktionen zu bieten. Das bezahlt man in C++ mit viel Klassenbeziehungs-Wirrwarr und im kernel nutzlosen Overhead.
Nur weil man C++ benutzt wird man aber nicht gezwungen komplizierte Klassenbeziehungen zu bauen oder Techniken zu nutzen die Overhead erzeugen. Und nutzlos ist es sicherlich nicht, selbst wenn es nur didaktischen Zwecken dient.
Es gibt leider auch viele Punkte, die man nur instinktiv richtig machen kann
Nach meiner Erfahrung sind Computer streng deterministische Gebilde, instinktive Entscheidungen sind daher nicht angebracht.
Erhard Henkes schrieb:
Das bezahlt man in C++ mit viel Klassenbeziehungs-Wirrwarr und im kernel nutzlosen Overhead.
Overhead gegenüber C gibt es in C++ erst wenn man virtuelle Methoden benutzt oder die Exceptions verwendet. Insofern spricht IMHO nichts gegen die Verwendung von C++ anstelle von C, in jedem Anwendungsfall auch in einem OS-Kernel, solange man nicht zu tief in die Trickkiste von C++ greift. Auch der erzeugte Programm-Code ist dann nicht größer/langsamer als der von C.
Zeige mir eine konkrete Stelle in PrettyOS im Kernel, bei der uns C++ einen greifbaren Vorteil bringen würde.
Ein technischer Grund fällt mir zurzeit nicht ein, manchmal ist es aber einfach eine andere Denkweise die hilft. Bei der Floppy gibt es z.B. vom Design her Probleme, die meiner Meinung nach objektorientiert eher auffallen und leicht zu lösen sind. Erfahrene C Entwickler werden diese Meinung sicherlich nicht teilen, da sie auch so die Probleme und Lösungen direkt sehen.
Ein Problem ist das eine mögliche Erweiterung auf mehrere Floppys unbequem ist. Man müsste um eine andere Floppy anzusprechen den Wert von _CurrentDrive umsetzen. Das sorgt aber dafür das sämtliche Befehle auf der anderen Floppy ausgeführt werden. Letztendlich würde man sich also an vielen Stellen den alten Wert merken, den Wert ändern, Befehle ausführen und den Wert wieder zurücksetzen. Ist jetzt kein Beinbruch, aber unschön wenn man das mal irgendwo vergisst. In C++ wäre man wahrscheinlich als erstes auf die Idee gekommen das ganze als Klasse zu modellieren. Dann steht jedes Objekt für eine Floppy und besitzt ein separates _CurrentDrive.
Das andere Problem ist die schon öfter erwähnte Treiberschnittstelle. In C++ würde man z.B. eine Klasse BlockDevice als Schnittstelle mit Funktionen wie read und write erstellen. Klasse Floppy, Festplatte etc. erben von BlockDevice und implementieren diese Funktionen. In einer Datenstruktur wie List<BlockDevice> kann man dann alle Geräte sammeln. In C wird dies nicht ganz so ausdrucksstark dargestellt. Statt der Klasse BlockDevice hat man eine Struktur mit Funktionspointern. Die bisherige Liste in PrettyOS arbeitet bisher auch nur mit void*. Es wird also auch noch öfters gecastet werden müssen. Es ist im Prinzip die gleiche Vorgehensweise wie in C++, allerdings finde ich die C++ Variante etwas anschaulicher.
#include "datatypes.h"
#ifdef MAX_MEMORY_BLOCKS
#error Already defined.
#else /// Specifies number of memory blocks used for dynamic memory
#define MAX_MEMORY_BLOCKS (32u)
#endif
#ifdef MAX_BLOCK_SIZE
#error Already defined.
#else /// Specifies number of uint32_t words contained in one memory block
#define MAX_BLOCK_SIZE (1023u)
#endif
/// Structure for memory block static struct tMemoryBlock
{
uint32_t used; ///< Flag, indicating if memory block is used or not
uint32_t data[MAX_BLOCK_SIZE]; ///< Data words of the memory block
}
MemoryBlock[MAX_MEMORY_BLOCKS];
/// Check size of one MemoryBlock structure. It is expected to be a fixed number of bytes. typedef char CheckSize_tMemoryBlock[sizeof(MemoryBlock[0]) == 4096 ? 1: -1];
// This loop goes through all memory blocks and looks for free one for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
if (0u == MemoryBlock[i].used)
{
// Found one unused memory block
// Set found index and break the loop
found_i = i;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
// Set used flag
++(MemoryBlock[found_i].used);
return &(MemoryBlock[found_i].data[0]);
}
void operator delete(void* pData)
{
uint32_t i = 0u;
// This loop goes through all memory blocks and checks, if
// the pointer points to it. The corresponding memory block
// gets unused then. for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
const void* pBegin = &(MemoryBlock[i].data[0]);
const void* pEnd = &(MemoryBlock[i].data[MAX_BLOCK_SIZE - 1u]);
if ((pData >= pBegin) && (pData <= pEnd))
{
// Found one used block, no sense to continue the loop
// Set it as unused and break the loop
MemoryBlock[i].used = 0;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
}
void* operator new(uint32_t size, void* pBuffer)
{
size = size;
// Because this function is "placement new", just return the
// pointer to the buffer return pBuffer;
}
#include "datatypes.h"
#ifdef MAX_MEMORY_BLOCKS
#error Already defined.
#else /// Specifies number of memory blocks used for dynamic memory
#define MAX_MEMORY_BLOCKS (32u)
#endif
#ifdef MAX_BLOCK_SIZE
#error Already defined.
#else /// Specifies number of uint32_t words contained in one memory block
#define MAX_BLOCK_SIZE (1023u)
#endif
/// Structure for memory block static struct tMemoryBlock
{
uint32_t used; ///< Flag, indicating if memory block is used or not
uint32_t data[MAX_BLOCK_SIZE]; ///< Data words of the memory block
}
MemoryBlock[MAX_MEMORY_BLOCKS];
/// Check size of one MemoryBlock structure. It is expected to be a fixed number of bytes. typedef char CheckSize_tMemoryBlock[sizeof(MemoryBlock[0]) == 4096 ? 1: -1];
// This loop goes through all memory blocks and looks for free one for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
if (0u == MemoryBlock[i].used)
{
// Found one unused memory block
// Set found index and break the loop
found_i = i;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
// Set used flag
++(MemoryBlock[found_i].used);
return &(MemoryBlock[found_i].data[0]);
}
void operator delete(void* pData)
{
uint32_t i = 0u;
// This loop goes through all memory blocks and checks, if
// the pointer points to it. The corresponding memory block
// gets unused then. for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
const void* pBegin = &(MemoryBlock[i].data[0]);
const void* pEnd = &(MemoryBlock[i].data[MAX_BLOCK_SIZE - 1u]);
if ((pData >= pBegin) && (pData <= pEnd))
{
// Found one used block, no sense to continue the loop
// Set it as unused and break the loop
MemoryBlock[i].used = 0;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
}
void* operator new(uint32_t size, void* pBuffer)
{
size = size;
// Because this function is "placement new", just return the
// pointer to the buffer return pBuffer;
}
#include "datatypes.h"
#ifdef MAX_MEMORY_BLOCKS
#error Already defined.
#else /// Specifies number of memory blocks used for dynamic memory
#define MAX_MEMORY_BLOCKS (32u)
#endif
#ifdef MAX_BLOCK_SIZE
#error Already defined.
#else /// Specifies number of uint32_t words contained in one memory block
#define MAX_BLOCK_SIZE (1023u)
#endif
/// Structure for memory block static struct tMemoryBlock
{
uint32_t used; ///< Flag, indicating if memory block is used or not
uint32_t data[MAX_BLOCK_SIZE]; ///< Data words of the memory block
}
MemoryBlock[MAX_MEMORY_BLOCKS];
/// Check size of one MemoryBlock structure. It is expected to be a fixed number of bytes. typedef char CheckSize_tMemoryBlock[sizeof(MemoryBlock[0]) == 4096 ? 1: -1];
// This loop goes through all memory blocks and looks for free one for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
if (0u == MemoryBlock[i].used)
{
// Found one unused memory block
// Set found index and break the loop
found_i = i;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
// Set used flag
++(MemoryBlock[found_i].used);
return &(MemoryBlock[found_i].data[0]);
}
void operator delete(void* pData)
{
uint32_t i = 0u;
// This loop goes through all memory blocks and checks, if
// the pointer points to it. The corresponding memory block
// gets unused then. for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
const void* pBegin = &(MemoryBlock[i].data[0]);
const void* pEnd = &(MemoryBlock[i].data[MAX_BLOCK_SIZE - 1u]);
if ((pData >= pBegin) && (pData <= pEnd))
{
// Found one used block, no sense to continue the loop
// Set it as unused and break the loop
MemoryBlock[i].used = 0;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
}
void* operator new(uint32_t size, void* pBuffer)
{
size = size;
// Because this function is "placement new", just return the
// pointer to the buffer return pBuffer;
}
In C++ würde man z.B. eine Klasse BlockDevice als Schnittstelle mit Funktionen wie read und write erstellen. Klasse Floppy, Festplatte etc. erben von BlockDevice und implementieren diese Funktionen. In einer Datenstruktur wie List<BlockDevice> kann man dann alle Geräte sammeln. In C wird dies nicht ganz so ausdrucksstark dargestellt. Statt der Klasse BlockDevice hat man eine Struktur mit Funktionspointern. Die bisherige Liste in PrettyOS arbeitet bisher auch nur mit void*. Es wird also auch noch öfters gecastet werden müssen. Es ist im Prinzip die gleiche Vorgehensweise wie in C++, allerdings finde ich die C++ Variante etwas anschaulicher.
Lese gerade zufällig in c't (Heft 19, 31.8.2009):
Zitat:
Kritische Lücke im Linux-Kernel
Eine Sicherheitslücke im Linux-Kernel betrifft alle Versionen ... seit Mai 2001. ... Üblicherweise deklariert eine Pointer-Struktur, welche Operationen ein Socket untersützt, etwa accept, bind und so weiter. Ist aber die Operation accept nicht implementiert, so sollte sie auf eine vordefinierte Komponente wie sock_no_accept zeigen. Dies ist offensichtlich nicht bei allen implementierten Protokollen der Fall...
So ist es, man hat ja keine Überwachung durch den Compiler, also muss der Programmierer selbst darauf achten, dass er alle Funktions-Pointer setzt bzw. auf deren Gültigkeit prüft...
Die Stärken moderner Programmiersprachen bestehen in der Fehlervermeidung und -überwachung. Allerdings hat man dafür bei C einen einfacheren Umgang mit der Materie. C++ fordert dem Entwickler hier deutlich mehr ab.
Warum nicht gleich Java für OSDEV?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Nächstes Thema anzeigen Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum nicht antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums
für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de
Werbekostenerstattung verdient werden kann.
Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de
enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt
(vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in
privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden.
Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere
übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.