आप C में क्लास कैसे लागू करते हैं? [बन्द है]


139

यह मानते हुए कि मुझे C (कोई C ++ या ऑब्जेक्ट ओरिएंटेड कंपाइलर) का उपयोग नहीं करना है और मेरे पास डायनेमिक मेमोरी एलोकेशन नहीं है, मैं कुछ तकनीकों का उपयोग कर सकता हूं जिन्हें मैं क्लास को लागू करने के लिए उपयोग कर सकता हूं, या क्लास का एक अच्छा सन्निकटन? क्या हमेशा "क्लास" को एक अलग फ़ाइल में अलग करना एक अच्छा विचार है? मान लें कि हम एक निश्चित संख्या में उदाहरणों को मानकर या प्रत्येक वस्तु के संदर्भ को संकलित समय से पहले स्थिर मानकर स्मृति का प्रचार कर सकते हैं। इस बात के लिए बेझिझक विचार करें कि मुझे किस ओओपी अवधारणा को लागू करने की आवश्यकता है (यह अलग-अलग होगी) और प्रत्येक के लिए सबसे अच्छी विधि का सुझाव दें।

प्रतिबंध:

  • मुझे C का उपयोग करना है और OOP का नहीं क्योंकि मैं एक एम्बेडेड सिस्टम के लिए कोड लिख रहा हूं, और C में कंपाइलर और preexisting कोड है।
  • कोई डायनेमिक मेमोरी एलोकेशन नहीं है क्योंकि हमारे पास यथोचित मेमोरी नहीं है कि अगर हम डायनामिकली एलोकेशन शुरू करते हैं तो हम यथोचित रूप से रन आउट नहीं होंगे।
  • संकलक हम साथ काम करते हैं, फ़ंक्शन पॉइंटर्स के साथ कोई समस्या नहीं है

26
अनिवार्य प्रश्न: क्या आपको ऑब्जेक्ट-ओरिएंटेड कोड लिखना है? यदि आप जो भी कारण के लिए करते हैं, वह ठीक है, लेकिन आप एक कठिन लड़ाई लड़ रहे होंगे। यह शायद सबसे अच्छा है यदि आप सी में ऑब्जेक्ट-ओरिएंटेड कोड लिखने से बचने की कोशिश कर रहे हैं, तो यह निश्चित रूप से संभव है - आराम के उत्कृष्ट उत्तर देखें - लेकिन यह बिल्कुल "आसान" नहीं है, और यदि आप सीमित मेमोरी के साथ एम्बेडेड सिस्टम पर काम कर रहे हैं, तो यह संभव नहीं है। मैं गलत हो सकता हूं, हालांकि - मैं आपको इसके बारे में बहस करने की कोशिश नहीं कर रहा हूं, बस कुछ काउंटरपॉइंट प्रस्तुत करें जो शायद प्रस्तुत नहीं किए गए हैं।
क्रिस लुत्ज

1
सख्ती से बोलना, हमें नहीं करना है। हालांकि, सिस्टम की जटिलता ने कोड को अकल्पनीय बना दिया है। मेरी भावना यह है कि जटिलता को कम करने का सबसे अच्छा तरीका कुछ ओओपी अवधारणाओं को लागू करना है। 3 मिनट के भीतर जवाब देने वाले सभी के लिए धन्यवाद। तुम लोग पागल और जल्दी हो!
बेन गार्टनर

8
यह सिर्फ मेरी विनम्र राय है, लेकिन OOP कोड को तुरंत बनाए रखने योग्य नहीं बनाता है। यह प्रबंधन करना आसान बना सकता है, लेकिन जरूरी नहीं कि अधिक रखरखाव योग्य हो। आपके पास सी में "नेमस्पेस" हो सकता है (अपाचे पोर्टेबल रनटाइम सभी ग्लोबल प्रतीकों के साथ apr_और GLib उन्हें g_एक नाम स्थान बनाने के लिए उपसर्ग करता है ) और अन्य आयोजन कारक OOP के बिना। यदि आप किसी भी तरह से एप्लिकेशन का पुनर्गठन करने जा रहे हैं, तो मैं कुछ समय के लिए एक अधिक रख-रखाव प्रक्रियात्मक संरचना के साथ आने की कोशिश करने पर विचार करूंगा।
क्रिस लुत्ज

इससे पहले अंतहीन चर्चा की जा चुकी है - क्या आपने पिछले उत्तरों में से किसी पर भी गौर किया है?
लैरी वतनबे

यह स्रोत, जो मेरे द्वारा हटाए गए उत्तर में था, मदद के लिए भी हो सकता है: planetpdf.com/codecuts/pdfs/ooc.pdf यह O में C. करने के लिए एक पूर्ण दृष्टिकोण का वर्णन करता है
Ruben Steins

जवाबों:


86

यह उस सटीक "ऑब्जेक्ट-ओरिएंटेड" फीचर-सेट पर निर्भर करता है, जिसे आप चाहते हैं। यदि आपको ओवरलोडिंग और / या आभासी तरीकों जैसे सामान की आवश्यकता है, तो आपको संभवतः संरचनाओं में फ़ंक्शन पॉइंटर्स शामिल करने की आवश्यकता है:

typedef struct {
  float (*computeArea)(const ShapeClass *shape);
} ShapeClass;

float shape_computeArea(const ShapeClass *shape)
{
  return shape->computeArea(shape);
}

यह आपको एक वर्ग को लागू करने देगा, बेस क्लास को "विरासत में" लेकर, और एक उपयुक्त कार्य को कार्यान्वित करेगा:

typedef struct {
  ShapeClass shape;
  float width, height;
} RectangleClass;

static float rectangle_computeArea(const ShapeClass *shape)
{
  const RectangleClass *rect = (const RectangleClass *) shape;
  return rect->width * rect->height;
}

इस कोर्स के लिए आपको एक कंस्ट्रक्टर को लागू करने की भी आवश्यकता होती है, जो यह सुनिश्चित करता है कि फ़ंक्शन पॉइंटर ठीक से सेट किया गया है। आम तौर पर आप गतिशील रूप से उदाहरण के लिए मेमोरी आवंटित करेंगे, लेकिन आप कॉलर को ऐसा करने दे सकते हैं:

void rectangle_new(RectangleClass *rect)
{
  rect->width = rect->height = 0.f;
  rect->shape.computeArea = rectangle_computeArea;
}

यदि आप कई अलग-अलग निर्माता चाहते हैं, तो आपको फ़ंक्शन नामों को "सजाना" होगा, आपके पास एक से अधिक rectangle_new()फ़ंक्शन नहीं हो सकते :

void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
  rectangle_new(rect);
  rect->width = width;
  rect->height = height;
}

यहाँ एक मूल उदाहरण दिखा रहा है:

int main(void)
{
  RectangleClass r1;

  rectangle_new_with_lengths(&r1, 4.f, 5.f);
  printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
  return 0;
}

मुझे उम्मीद है कि यह आपको कुछ विचार देता है, कम से कम। सी में एक सफल और समृद्ध ऑब्जेक्ट-ओरिएंटेड फ्रेमवर्क के लिए, ग्लिब के गोबजेक्ट लाइब्रेरी में देखें।

यह भी ध्यान दें कि ऊपर कोई स्पष्ट "वर्ग" नहीं है, प्रत्येक वस्तु के अपने तरीके के संकेत होते हैं जो कि आपके द्वारा आमतौर पर C ++ में प्राप्त किए जाने की तुलना में थोड़ा अधिक लचीला होता है। इसके अलावा, यह स्मृति लागत। आप एक classसंरचना में विधि बिंदुओं को भरकर उस से दूर हो सकते हैं , और प्रत्येक वस्तु उदाहरण के लिए एक वर्ग को संदर्भित करने के लिए एक तरीका का आविष्कार कर सकते हैं।


ऑब्जेक्ट-ओरिएंटेड सी लिखने की कोशिश नहीं करने के बाद, क्या यह आमतौर पर उन कार्यों को करने के लिए सबसे अच्छा है जो तर्क const ShapeClass *या const void *तर्क के रूप में लेते हैं ? ऐसा लगता है कि उत्तरार्द्ध विरासत पर थोड़ा अच्छा हो सकता है, लेकिन मैं दोनों तरीकों से तर्क देख सकता हूं ...
क्रिस लुट्ज़

1
@ क्रिस: हाँ, यह एक कठिन सवाल है। : | GTK + (जो GObject का उपयोग करता है) उचित वर्ग का उपयोग करता है, अर्थात रेक्टेंगलक्लास *। इसका मतलब है कि आपको अक्सर कास्ट करना पड़ता है, लेकिन वे मैक्रो प्रदान करते हैं, जिससे आपको मदद मिलती है, इसलिए आप हमेशा BASECLASS * p से SUBCLASS * को सिर्फ SUBCLASS (p) का उपयोग करके डाल सकते हैं।
खोलना

1
मेरा संकलक कोड की दूसरी पंक्ति पर विफल रहता है: float (*computeArea)(const ShapeClass *shape);यह कहना कि ShapeClassएक अज्ञात प्रकार है।
डैनियलस्कैन

@DanielSank 'टाइपराइफ स्ट्रक्चर' (उदाहरण में नहीं दिखाया गया है) द्वारा आवश्यक फॉरवर्ड घोषणा की कमी के कारण है। क्योंकि structस्वयं संदर्भ, इसे परिभाषित करने से पहले एक घोषणा की आवश्यकता है। इसे लुंडिन के उत्तर में एक उदाहरण के साथ समझाया गया है । आगे की घोषणा को शामिल करने के लिए उदाहरण को संशोधित करके आपके मुद्दे को हल करना चाहिए; typedef struct ShapeClass ShapeClass; struct ShapeClass { float (*computeArea)(const ShapeClass *shape); };
एस।

क्या होता है जब आयत में एक फ़ंक्शन होता है जो सभी आकार नहीं करते हैं। उदाहरण के लिए, get_corners ()। एक सर्कल इसे लागू नहीं करेगा लेकिन एक आयत हो सकता है। आप उस फ़ंक्शन का उपयोग कैसे करते हैं जो उस मूल वर्ग का हिस्सा नहीं है जो आपको विरासत में मिला है?
ओटस

24

मुझे इसे एक बार होमवर्क के लिए भी करना पड़ा। मैंने इस दृष्टिकोण का अनुसरण किया:

  1. एक संरचना में अपने डेटा सदस्यों को परिभाषित करें।
  2. अपने फ़ंक्शन के सदस्यों को परिभाषित करें जो पहले तर्क के रूप में आपकी संरचना के लिए एक संकेतक लेते हैं।
  3. इन्हें एक हेडर और एक सी में करें। कार्यान्वयन के लिए संरचना की परिभाषा और फ़ंक्शन घोषणाओं के लिए हेडर, सी।

एक सरल उदाहरण यह होगा:

/// Queue.h
struct Queue
{
    /// members
}
typedef struct Queue Queue;

void push(Queue* q, int element);
void pop(Queue* q);
// etc.
/// 

यह वही है जो मैंने अतीत में किया है, लेकिन फ़ंक्शन प्रोटोटाइप को या तो .c या .h फ़ाइल में आवश्यकतानुसार रखकर फेकिंग स्कोप के साथ (जैसा कि मैंने अपने उत्तर में उल्लेख किया है)।
टेलर लेसे

मुझे यह पसंद है, संरचना की घोषणा सभी मेमोरी को आवंटित करती है। किसी कारण से मैं भूल गया कि यह अच्छा काम करेगा।
बेन गार्टनर

मुझे लगता है कि आपको typedef struct Queue Queue;वहां जाने की जरूरत है ।
क्रेग मैकक्वीन

3
या सिर्फ टाइपडिफ संरचना {/ * सदस्य * /} कतार;
ब्रूक्स मूसा

# क्रेग: अनुस्मारक के लिए धन्यवाद, अद्यतन।
इरेलेंडर

12

यदि आप केवल एक वर्ग चाहते हैं, structतो "ऑब्जेक्ट्स" डेटा के रूप में एस की एक सरणी का उपयोग करें और "सदस्य" फ़ंक्शन को उन्हें पास करें। आप क्लाइंट कोड से कार्यान्वयन को छिपाने के लिए typedef struct _whatever Whateverघोषणा struct _whateverकरने से पहले उपयोग कर सकते हैं । इस तरह के "ऑब्जेक्ट" और सी मानक लाइब्रेरी FILEऑब्जेक्ट के बीच कोई अंतर नहीं है ।

यदि आप इनहेरिटेंस और वर्चुअल फ़ंक्शंस के साथ एक से अधिक क्लास चाहते हैं, तो यह स्ट्रक्चर के सदस्यों के रूप में फ़ंक्शंस के लिए पॉइंटर्स, या वर्चुअल फ़ंक्शंस की एक साझा पॉइंटर के लिए एक साझा पॉइंटर होना आम है। GObject पुस्तकालय दोनों इस और typedef चाल का उपयोग करता है, और व्यापक रूप से प्रयोग किया जाता है।

इस उपलब्ध ऑनलाइन के लिए तकनीकों पर एक पुस्तक भी है - ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग एएनएसआई सी के साथ


1
ठंडा! सी में ओओपी पर पुस्तकों के लिए कोई अन्य सिफारिशें? या सी में कोई अन्य आधुनिक डिजाइन तकनीक? (या एम्बेडेड सिस्टम?)
बेन गार्टनर

7

आप GOBject पर एक नज़र डाल सकते हैं। यह एक ओएस लाइब्रेरी है जो आपको एक ऑब्जेक्ट करने के लिए एक वर्बोज़ तरीका देता है।

http://library.gnome.org/devel/gobject/stable/


1
बहुत दिलचस्प। किसी को भी लाइसेंस के बारे में पता है? काम पर मेरे उद्देश्यों के लिए, एक परियोजना में एक खुला स्रोत पुस्तकालय छोड़ने शायद एक कानूनी दृष्टिकोण से काम नहीं करने वाला है।
बेन गार्टनर

GTK +, और सभी लाइब्रेरी जो उस प्रोजेक्ट का हिस्सा हैं (GObject सहित), GNU LGPL के तहत लाइसेंस प्राप्त हैं, जिसका अर्थ है कि आप उन्हें मालिकाना सॉफ्टवेयर से लिंक कर सकते हैं। मुझे नहीं पता कि यह एम्बेडेड काम के लिए संभव होने जा रहा है, हालांकि।
क्रिस लुत्ज

7

सी इंटरफेसेस एंड इम्प्लीमेंटेशन: रीक्रिएबल सॉफ्टवेयर बनाने की तकनीक , डेविड आर। हैंसन

http://www.informit.com/store/product.aspx?isbn=0201498413

यह पुस्तक आपके प्रश्न को कवर करने का एक उत्कृष्ट काम करती है। यह एडिसन वेस्ले प्रोफेशनल कम्प्यूटिंग श्रृंखला में है।

मूल प्रतिमान कुछ इस तरह है:

/* for data structure foo */

FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);

5

मैं एक सरल उदाहरण दूंगा कि ओओपी को सी में कैसे किया जाना चाहिए। मुझे एहसास है कि यह 2009 से है लेकिन फिर भी इसे जोड़ना चाहते हैं।

/// Object.h
typedef struct Object {
    uuid_t uuid;
} Object;

int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);

/// Person.h
typedef struct Person {
    Object obj;
    char *name;
} Person;

int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);

/// Object.c
#include "object.h"

int Object_init(Object *self)
{
    self->uuid = uuid_new();

    return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
    return self->uuid;
}
int Object_clean(Object *self)
{
    uuid_free(self->uuid);

    return 0;
}

/// Person.c
#include "person.h"

int Person_init(Person *self, char *name)
{
    Object_init(&self->obj); // Or just Object_init(&self);
    self->name = strdup(name);

    return 0;
}
int Person_greet(Person *self)
{
    printf("Hello, %s", self->name);

    return 0;
}
int Person_clean(Person *self)
{
    free(self->name);
    Object_clean(self);

    return 0;
}

/// main.c
int main(void)
{
    Person p;

    Person_init(&p, "John");
    Person_greet(&p);
    Object_get_uuid(&p); // Inherited function
    Person_clean(&p);

    return 0;
}

मूल अवधारणा में संरचना के शीर्ष पर 'विरासत में मिला वर्ग' रखना शामिल है। इस तरह, संरचना में पहले 4 बाइट्स तक पहुँचने से पहले 4 बाइट्स भी 'इनहेरिट की गई क्लास' (असमान गैर-पागल इष्टतम) को एक्सेस करती हैं। अब, जब संरचना के पॉइंटर को 'विरासत में मिला वर्ग' में डाला जाता है, तो 'विरासत में मिला वर्ग' उसी तरह 'विरासत में मिले मूल्यों' तक पहुंच सकता है, जिस तरह से यह सामान्य रूप से सदस्यों तक पहुंचेगा।

यह और कंस्ट्रक्टर्स, डिस्ट्रक्टर्स, एलोकेशन और डीललॉकरियन फ़ंक्शंस के लिए कुछ नामकरण सम्मेलनों (मैं init, clean, new, free की सलाह देता हूं) से आपको एक लंबा रास्ता मिल जाएगा।

वर्चुअल फ़ंक्शंस के लिए, संभवतः संरचनात्मक रूप से Class_func (...) के साथ फ़ंक्शन पॉइंटर्स का उपयोग करें; आवरण भी। के रूप में (सरल) टेम्पलेट्स के लिए, आकार निर्धारित करने के लिए size_t पैरामीटर जोड़ें, एक शून्य * पॉइंटर की आवश्यकता होती है, या आपको जिस कार्यक्षमता की परवाह है उसके साथ एक 'वर्ग' प्रकार की आवश्यकता होती है। (उदाहरण के लिए GetUUID (ऑब्जेक्ट * स्वयं); GetUUID (& p);)


अस्वीकरण: सभी कोड स्मार्टफोन पर लिखे गए। जहाँ आवश्यक हो, उसमें एरर-चेक जोड़ें। बग के लिए जाँच करें।
yyny

4

structकिसी वर्ग के डेटा सदस्यों का अनुकरण करने के लिए एक का उपयोग करें । विधि क्षेत्र के संदर्भ में आप .c फ़ाइल में निजी फ़ंक्शन प्रोटोटाइप और पब्लिक फ़ंक्शंस को रखकर निजी तरीकों का अनुकरण कर सकते हैं ।


4
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>

/**
 * Define Shape class
 */
typedef struct Shape Shape;
struct Shape {
    /**
     * Variables header...
     */
    double width, height;

    /**
     * Functions header...
     */
    double (*area)(Shape *shape);
};

/**
 * Functions
 */
double calc(Shape *shape) {
        return shape->width * shape->height;
}

/**
 * Constructor
 */
Shape _Shape() {
    Shape s;

    s.width = 1;
    s.height = 1;

    s.area = calc;

    return s;
}

/********************************************/

int main() {
    Shape s1 = _Shape();
    s1.width = 5.35;
    s1.height = 12.5462;

    printf("Hello World\n\n");

    printf("User.width = %f\n", s1.width);
    printf("User.height = %f\n", s1.height);
    printf("User.area = %f\n\n", s1.area(&s1));

    printf("Made with \xe2\x99\xa5 \n");

    return 0;
};

3

आपके मामले में कक्षा का अच्छा सन्निकटन एक ADT हो सकता है । लेकिन फिर भी यह वैसा नहीं होगा।


1
क्या कोई अमूर्त डेटा प्रकार और एक वर्ग के बीच एक संक्षिप्त अंतर दे सकता है? मैंने हमेशा दो अवधारणाओं को बारीकी से जोड़ा है।
बेन गार्टनर

वे वास्तव में निकटता से संबंधित हैं। एक वर्ग को एडीटी के कार्यान्वयन के रूप में देखा जा सकता है, क्योंकि (माना जाता है) इसे उसी इंटरफ़ेस को संतुष्ट करने वाले दूसरे कार्यान्वयन द्वारा प्रतिस्थापित किया जा सकता है। मुझे लगता है कि अवधारणाओं को स्पष्ट रूप से परिभाषित नहीं किया गया है, हालांकि एक सटीक अंतर देना मुश्किल है।
जोर्जेन फॉग

3

मेरी रणनीति है:

  • एक अलग फ़ाइल में वर्ग के लिए सभी कोड को परिभाषित करें
  • एक अलग हेडर फ़ाइल में वर्ग के लिए सभी इंटरफेस को परिभाषित करें
  • सभी सदस्य फ़ंक्शन एक "क्लासहैंडल" लेते हैं, जो उदाहरण के नाम (o.foo () के बजाय, foo (oHandle) के लिए आता है)
  • कंस्ट्रक्टर को एक फ़ंक्शन void ClassInit (ClassHandle h, int x, int y, ...) या ClassHandle ClassInit (int x, int y, ...) के साथ मेमोरी आवंटन रणनीति के आधार पर बदल दिया जाता है।
  • सभी सदस्य चर वर्ग फ़ाइल में एक स्थिर संरचना के एक सदस्य के रूप में संग्रहीत किए जाते हैं, इसे फ़ाइल में एन्क्रिप्ट किया जाता है, इसे एक्सेस करने से बाहर की फ़ाइलों को रोका जाता है।
  • ऑब्जेक्ट्स को स्थिर संरचना के एक सरणी में संग्रहीत किया जाता है, पूर्वनिर्धारित हैंडल (इंटरफ़ेस में दिखाई देता है) या ऑब्जेक्ट की एक निश्चित सीमा जो कि तत्काल हो सकती है
  • यदि उपयोगी है, तो क्लास में सार्वजनिक फ़ंक्शन हो सकते हैं जो सरणी के माध्यम से लूप करेंगे और सभी तात्कालिक वस्तुओं के कार्यों को कॉल करेंगे (RunAll () कॉल प्रत्येक रन (oHandle)
  • एक डीइनिट (क्लासहैंडल एच) फ़ंक्शन डायनेमिक आवंटन रणनीति में आवंटित मेमोरी (एरे इंडेक्स) को मुक्त करता है

क्या किसी को भी इस दृष्टिकोण की भिन्नता के लिए कोई समस्या, छिद्र, संभावित नुकसान या छिपे लाभ / कमियां दिखाई देती हैं? यदि मैं एक डिज़ाइन विधि को पुन: लागू कर रहा हूं (और मुझे लगता है कि मुझे होना चाहिए), तो क्या आप मुझे इसके नाम पर इंगित कर सकते हैं?


एक शैली के रूप में, यदि आपके पास अपने प्रश्न को जोड़ने के लिए जानकारी है, तो आपको इस जानकारी को शामिल करने के लिए अपने प्रश्न को संपादित करना चाहिए।
क्रिस लुट्ज़

आपको मालॉक से गतिशील रूप से एक बड़े ढेर से ClassInit में आवंटित किया गया प्रतीत होता है () गतिशील रूप से एक निश्चित आकार के पूल से चयन करने के बजाय, वास्तव में कुछ भी करने के बारे में कुछ भी करने से क्या होगा जब आप किसी अन्य ऑब्जेक्ट के लिए पूछते हैं और आपके पास एक प्रदान करने के लिए संसाधन नहीं होते हैं ।
पीट किर्कम

हाँ, स्मृति प्रबंधन का बोझ क्लासइनिट () को कॉल करने वाले कोड पर स्थानांतरित कर दिया जाता है ताकि यह पता चल सके कि लौटा हुआ हैंडल वैध है। अनिवार्य रूप से हमने कक्षा के लिए अपना खुद का समर्पित ढेर बनाया है। सुनिश्चित नहीं है कि हम इससे बचने का कोई रास्ता देखते हैं यदि हम कोई गतिशील आवंटन करना चाहते हैं, जब तक कि हम एक सामान्य उद्देश्य को लागू न करें। मैं एक वर्ग में हीप में निहित जोखिम को अलग करना चाहूंगा।
बेन गार्टनर

3

इस उत्तर को और इस एक को भी देखें

हो सकता है। यह हमेशा उस समय एक अच्छा विचार लगता है लेकिन बाद में यह एक बुरा सपना बन जाता है। आपका कोड सब कुछ एक साथ बांधने वाले कोड के टुकड़ों से अटे पड़े हैं। एक नए प्रोग्रामर को कोड को पढ़ने और समझने में बहुत सारी समस्याएं होंगी यदि आप फ़ंक्शन पॉइंटर्स का उपयोग करते हैं क्योंकि यह स्पष्ट नहीं होगा कि फ़ंक्शन क्या कहा जाता है।

प्राप्त / सेट फ़ंक्शंस के साथ डेटा छिपाना C में लागू करना आसान है, लेकिन वहाँ रुकना। मैंने एम्बेडेड वातावरण में इस पर कई प्रयास देखे हैं और अंत में यह हमेशा रखरखाव की समस्या है।

चूंकि आप सभी तैयार हैं रखरखाव के मुद्दे मैं स्पष्ट होगा।


2

मेरा दृष्टिकोण एक अलग स्रोत फ़ाइल (ओं) को structसभी और मुख्य रूप से जुड़े कार्यों को स्थानांतरित करने के लिए होगा ताकि इसका उपयोग "पोर्टली" किया जा सके।

अपने संकलक के आधार पर आप हो सकता है में कार्यों में शामिल हैं करने में सक्षम हो struct, लेकिन यह एक है बहुत संकलक-विशिष्ट एक्सटेंशन, और मानक मैं नियमित तौर पर इस्तेमाल के पिछले संस्करण के साथ कोई संबंध नहीं है :)


2
फंक्शन पॉइंटर्स सभी अच्छे हैं। हम लुकअप टेबल के साथ बड़े स्विच स्टेटमेंट को बदलने के लिए उनका उपयोग करते हैं।
बेन गार्टनर

2

पहला c ++ कंपाइलर वास्तव में एक प्रीप्रोसेसर था जिसने C ++ कोड को C में अनुवाद किया।

इसलिए सी में कक्षाएं लेना बहुत संभव है। आप एक पुराने सी ++ प्रीप्रोसेसर को आज़मा सकते हैं और देख सकते हैं कि यह किस तरह का समाधान बनाता है।


वह होगा cfront; C ++ में अपवाद जोड़े जाने पर यह समस्याओं में भाग गया - अपवादों को संभालना तुच्छ नहीं है।
जोनाथन लेफ़लर

2

GTK पूरी तरह से C पर बना है और यह कई OOP अवधारणाओं का उपयोग करता है। मैंने GTK के स्रोत कोड के माध्यम से पढ़ा है और यह बहुत प्रभावशाली है, और निश्चित रूप से पढ़ने में आसान है। मूल अवधारणा यह है कि प्रत्येक "वर्ग" बस एक संरचना है, और संबंधित स्थिर कार्य हैं। स्थैतिक कार्य सभी एक पैरामीटर के रूप में "उदाहरण" संरचना को स्वीकार करते हैं, फिर जो भी आवश्यक हो, करते हैं और यदि आवश्यक हो तो परिणाम वापस करते हैं। उदाहरण के लिए, आपके पास "GetPosition (CircleStruct obj)" एक फ़ंक्शन हो सकता है। फ़ंक्शन केवल संरचना के माध्यम से खोदता है, स्थिति संख्याओं को निकालता है, शायद एक नया पोजिशनस्ट्रक्ट ऑब्जेक्ट का निर्माण करेगा, एक्स और वाई को नए पॉजिस्टस्ट्रक्ट में चिपकाएगा, और इसे वापस कर देगा। GTK यहां तक ​​कि स्ट्रक्चर्स के अंदर स्ट्रक्चर्स को इनहेरिट करके इस तरह इम्प्लीमेंट करता है। बहुत चालाक है।


1

क्या आप आभासी तरीके चाहते हैं?

यदि नहीं, तो आप केवल संरचना में फ़ंक्शन पॉइंटर्स के एक सेट को परिभाषित करते हैं। यदि आप सभी फ़ंक्शन पॉइंटर्स को मानक C फ़ंक्शन पर असाइन करते हैं, तो आप C से फ़ंक्शन को बहुत समान सिंटैक्स में कॉल कर पाएंगे कि आप C ++ के अंतर्गत कैसे आएंगे।

यदि आप आभासी तरीके रखना चाहते हैं तो यह अधिक जटिल हो जाता है। मूल रूप से आपको प्रत्येक संरचना के लिए अपने स्वयं के वीटीएबल को लागू करने की आवश्यकता होगी और उस फ़ंक्शन के आधार पर वीटीएबल को फ़ंक्शन पॉइंटर्स असाइन करना होगा। आपको तब संरचना में फ़ंक्शन पॉइंटर्स के एक सेट की आवश्यकता होगी जो बदले में VTable में फ़ंक्शन पॉइंटर को कॉल करेगा। यह अनिवार्य रूप से, C ++ क्या करता है।

टीबीएच हालांकि ... यदि आप बाद चाहते हैं, तो आप शायद सी ++ संकलक खोजने के लिए बेहतर हैं जो आप उपयोग कर सकते हैं और परियोजना को फिर से संकलित कर सकते हैं। मैं कभी नहीं समझ पाया C ++ के साथ जुनून एम्बेडेड में प्रयोग करने योग्य नहीं है। मैंने इसे कई बार उपयोग किया है और यह तेज़ है और इसमें मेमोरी की समस्या नहीं है। यकीन है कि आप जो करते हैं उसके बारे में थोड़ा और अधिक सावधान रहना होगा लेकिन वास्तव में यह उतना जटिल नहीं है।


मैंने कहा है कि यह पहले से ही है और इसे फिर से कहेगा, लेकिन इसे फिर से कहेंगे: आपको फ़ंक्शन पॉइंटर्स की आवश्यकता नहीं है या C में OOP बनाने के लिए स्ट्रक्चर्स C ++ स्टाइल से फ़ंक्शन को कॉल करने की क्षमता है, OOP ज्यादातर कार्यक्षमता और चर की विरासत के बारे में है (सामग्री) जो दोनों बिना फ़ंक्शन पॉइंटर्स या डुप्लिकेट कोड के C में प्राप्त की जा सकती हैं।
yyny

0

सी एक ओओपी भाषा नहीं है, जैसा कि आपका सही संकेत है, इसलिए एक सच्चा वर्ग लिखने का कोई अंतर्निहित तरीका नहीं है। आप सबसे अच्छी शर्त हैं कि आप स्ट्रक्चर्स , और फंक्शन पॉइंटर्स को देखें , ये आपको एक क्लास के अंदाज का निर्माण करने देंगे। हालाँकि, सी प्रक्रियात्मक है, इसलिए आप अधिक सी-लाइक कोड लिखने पर विचार कर सकते हैं (जैसे कि कक्षाओं का उपयोग किए बिना)।

इसके अलावा, यदि आप C का उपयोग कर सकते हैं, तो आप संभवतः C ++ का उपयोग कर सकते हैं और कक्षाएं प्राप्त कर सकते हैं।


4
मैं डाउनवोट नहीं करूंगा, लेकिन FYI, फंक्शन पॉइंटर्स, या स्ट्रक्चर्स से फ़ंक्शन को कॉल करने की क्षमता (जो मुझे लगता है कि आपका इरादा है) का OOP से कोई लेना-देना नहीं है। OOP ज्यादातर कार्यक्षमता और चर की विरासत के बारे में है, जो दोनों बिना फ़ंक्शन पॉइंटर्स या दोहराव के C में प्राप्त किया जा सकता है।
yyny
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.