क्या वस्तु उन्मुख सी लिखना बुरा है? [बन्द है]


14

मुझे हमेशा सी में कोड लिखना प्रतीत होता है जो ज्यादातर ऑब्जेक्ट ओरिएंटेड है, इसलिए कहते हैं कि मेरे पास एक स्रोत फ़ाइल थी या ऐसा कुछ जो मैं एक संरचना बनाऊंगा, इस संरचना के स्वामित्व वाले कार्यों (विधियों) के लिए इस संरचना को पॉइंटर पास करें:

struct foo {
    int x;
};

struct foo* createFoo(); // mallocs foo

void destroyFoo(struct foo* foo); // frees foo and its things

क्या यह बुरा अभ्यास है? मैं सी को "उचित तरीका" कैसे लिखना सीखता हूं।


10
अधिकांश लिनक्स (कर्नेल) को इस तरह से लिखा जाता है, वास्तव में यह और भी अधिक ओओ-जैसी अवधारणाओं जैसे आभासी विधि प्रेषण का अनुकरण करता है। मुझे लगता है कि बहुत उचित है।
किलियन फोथ २ K

13
" [T] उन्होंने निर्धारित किया कि रियल प्रोग्रामर किसी भी भाषा में फोरट्रान प्रोग्राम लिख सकता है। " - एड पोस्ट, 1983
रॉस पैटरसन

4
क्या कोई कारण है कि आप C ++ पर स्विच नहीं करना चाहते हैं? आपको इसके उन हिस्सों का उपयोग नहीं करना है जो आपको पसंद नहीं हैं।
svick

5
यह वास्तव में, "वस्तु उन्मुख 'क्या है?" मैं इस वस्तु को उन्मुख नहीं कहूंगा। मैं कहूंगा कि यह प्रक्रियात्मक है। (आपके पास कोई विरासत नहीं है, कोई बहुरूपता नहीं है, कोई छिपाने की क्षमता नहीं है / राज्य को छिपाने की क्षमता है, और शायद ओओ के अन्य हॉलमार्क गायब हैं जो मेरे सिर के ऊपर से नहीं निकल रहे हैं।) क्या यह अच्छा या बुरा अभ्यास उन शब्दार्थों पर निर्भर नहीं है। , हालांकि।
jpmc26

3
@ jpmc26: यदि आप एक भाषाई अभिभाषक हैं, तो आपको एलन के को सुनना चाहिए, उन्होंने इस शब्द का आविष्कार किया, उनका कहना है कि इसका क्या अर्थ है, और उनका कहना है कि OOP सभी संदेश के बारे में है । यदि आप एक भाषाई वर्णनात्मक हैं, तो आप सॉफ्टवेयर विकास समुदाय में शब्द के उपयोग का सर्वेक्षण करेंगे। कुक ने ठीक यही किया, उन्होंने उन भाषाओं की विशेषताओं का विश्लेषण किया जो या तो दावा करती हैं या जिन्हें OO माना जाता है, और उन्होंने पाया कि उनमें एक चीज समान है: संदेश
जोर्ग डब्ल्यू मित्तग

जवाबों:


24

नहीं, यह बुरा अभ्यास नहीं है, ऐसा करने के लिए भी प्रोत्साहित किया जाता है, हालांकि कोई भी सम्मेलनों का उपयोग कर सकता है जैसे struct foo *foo_new();औरvoid foo_free(struct foo *foo);

बेशक, जैसा कि एक टिप्पणी कहती है, केवल यही करें जहां उपयुक्त हो। एक के लिए एक निर्माता का उपयोग करने में कोई मतलब नहीं है int

उपसर्ग foo_बहुत सारे पुस्तकालयों के बाद एक सम्मेलन है, क्योंकि यह अन्य पुस्तकालयों के नामकरण के साथ टकराव के खिलाफ गार्ड है। अन्य कार्यों में अक्सर उपयोग करने के लिए सम्मेलन होता है foo_<function>(struct foo *foo, <parameters>);। यह आपके struct fooएक अपारदर्शी प्रकार होने की अनुमति देता है ।

सम्मेलन के लिए libcurl दस्तावेज़ीकरण पर एक नज़र डालें , विशेष रूप से "सबनामस्पेस" के साथ, ताकि curl_multi_*पहले पैरामीटर द्वारा वापस आने पर एक फ़ंक्शन को कॉल करना पहली नज़र में गलत लगता है curl_easy_init()

और भी अधिक सामान्य दृष्टिकोण हैं, ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग विथ ANSI-C देखें


11
हमेशा कैवेट के साथ "जहां उपयुक्त हो"। OOP चांदी की गोली नहीं है।
डेडुप्लिकेटर

क्या C में ऐसे नामस्थान नहीं हैं जिनमें आप इन कार्यों की घोषणा कर सकते हैं? के समान std::string, आपके पास नहीं हो सकता है foo::create? मैं सी का उपयोग नहीं करता हूं। शायद वह केवल सी ++ में है?
क्रिस क्रेफ़िस

@ChrisCirefice सी में कोई नामस्थान नहीं हैं, यही कारण है कि कई पुस्तकालय लेखक अपने कार्यों के लिए उपसर्ग का उपयोग करते हैं।
रेसुमुन

2

यह बुरा नहीं है, यह उत्कृष्ट है। ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग (जब तक आप दूर ले जाते हैं, आप एक अच्छी बात है सकते हैं बहुत ज्यादा एक अच्छी बात की है)। C OOP के लिए सबसे उपयुक्त भाषा नहीं है, लेकिन इससे आपको सबसे अच्छा मिलना बंद नहीं होना चाहिए।


4
ऐसा नहीं है कि मैं असहमत हो सकता हूं, लेकिन आपकी राय वास्तव में कुछ विस्तार द्वारा समर्थित होनी चाहिए ।
डेडुप्लिकेटर

1

यह बुरा नहीं है। यह RAII का उपयोग करने के लिए समर्थन करता है जो कई बग्स (मेमोरी लीक, असिंचित चर का उपयोग करना, मुफ्त के बाद उपयोग करना आदि) जो सुरक्षा के मुद्दों का कारण बन सकता है) को रोकता है।

इसलिए, यदि आप अपना कोड केवल GCC या Clang के साथ संकलित करना चाहते हैं (और MS संकलक के साथ नहीं), तो आप cleanupविशेषता का उपयोग कर सकते हैं , जो आपकी वस्तुओं को ठीक से नष्ट कर देगा। यदि आप अपनी वस्तु को इस तरह घोषित करते हैं:

my_str __attribute__((cleanup(my_str_destructor))) ptr;

तब my_str_destructor(ptr)चलाया जाएगा जब ptr दायरे से बाहर चला जाता है। बस यह ध्यान रखें, कि इसका उपयोग फ़ंक्शन के तर्कों के साथ नहीं किया जा सकता है

इसके अलावा, my_str_अपने विधि नामों में उपयोग करने के लिए याद रखें , क्योंकि नाम Cस्थान नहीं है, और यह कुछ अन्य फ़ंक्शन नाम से टकराना आसान है।


2
Afaik, RAII स्पष्ट संसाधन रिलीज़ कॉल जोड़ने की आवश्यकता से बचने, सफाई सुनिश्चित करने के लिए C ++ में ऑब्जेक्ट्स के लिए विध्वंसक के अंतर्निहित कॉलिंग का उपयोग करने के बारे में है। इसलिए, अगर मैं ज्यादा गलत नहीं हूं, तो RAII और C परस्पर अनन्य हैं।
सेंटास्टर - मोनिका

@cmaster यदि आप #defineअपने टाइपनेम का उपयोग करते हैं __attribute__((cleanup(my_str_destructor)))तो आपको इसे पूरे #defineदायरे में निहित किया जाएगा (इसे आपकी सभी परिवर्तनीय घोषणाओं में जोड़ा जाएगा)।
मारकिन

यदि आप a) आप gcc, b) का उपयोग करते हैं तो आप फंक्शन लोकल वैरिएबल्स में टाइप का उपयोग करते हैं, और c) यदि आप केवल एक नग्न संस्करण में टाइप करते हैं तो इसका उपयोग नहीं करते हैं #define। संक्षेप में: यह मानक C नहीं है, और आप उपयोग में बहुत अधिक अनम्यता के साथ भुगतान करते हैं।
सेंटास्टर -

जैसा कि मेरे उत्तर में बताया गया है, यह भी क्लैंग में काम करता है।
मारकिन

आह, मैंने ध्यान नहीं दिया। यह वास्तव में आवश्यकता को क) काफी कम गंभीर बना देता है, क्योंकि यह __attribute__((cleanup()))बहुत अधिक अर्ध-मानक बनाता है। हालाँकि, b) और c) अभी भी खड़े हैं ...
cmaster - reicaate monica

-2

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

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

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

उदाहरण के अनुसार, पारंपरिक संकलक पर, ऐतिहासिक संकलक निम्नलिखित कोड की व्याख्या करेंगे:

struct pair { int i1,i2; };
struct trio { int i1,i2,i3; };

void hey(struct pair *p, struct trio *t)
{
  p->i1++;
  t->i1^=1;
  p->i1--;
  t->i1^=1;
}

क्रम में निम्नलिखित चरणों का पालन करते हुए: पहले सदस्य का वेतन वृद्धि करें, पहले सदस्य के *pसबसे कम बिट का पूरक करें *t, फिर पहले सदस्य का वेतन वृद्धि करें और पहले सदस्य के सबसे कम बिट को *pपूरक करें *t। आधुनिक कंपाइलर एक फैशन में संचालन के अनुक्रम को फिर से व्यवस्थित करेंगे जो कोड है जो अधिक कुशल होगा यदि pऔर tविभिन्न वस्तुओं की पहचान करें, लेकिन यदि वे नहीं करते हैं तो व्यवहार को बदल देगा।

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

यदि किसी व्यक्ति को दो बिंदुओं की अदला-बदली के लिए स्वैप करने के लिए एक फ़ंक्शन लिखना चाहता था तो कुछ कम वंचित उदाहरण होगा। "1990 के दशक सी" कंपाइलरों के विशाल बहुमत में, जिसे पूरा किया जा सकता है:

void swap_pointers(void **p1, void **p2)
{
  void *temp = *p1;
  *p1 = *p2;
  *p2 = temp;
}

मानक सी में, हालांकि, एक का उपयोग करना होगा:

#include "string.h"
#include "stdlib.h"
void swap_pointers2(void **p1, void **p2)
{
  void **temp = malloc(sizeof (void*));
  memcpy(temp, p1, sizeof (void*));
  memcpy(p1, p2, sizeof (void*));
  memcpy(p2, temp, sizeof (void*));
  free(temp);
}

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


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

हम्म ... मन आप दिखाते हैं कि कैसे मानक गारंटी देता है जो आपको सामान्य प्रारंभिक उप-अनुक्रम के सदस्यों तक सफाई से पहुंचने की अनुमति नहीं देता है? क्योंकि मुझे लगता है कि इस समय में संविदात्मक व्यवहार की सीमा के भीतर अनुकूलन करने की हिम्मत की बुराइयों के बारे में आपका क्या कहना है? (यह मेरा अनुमान है कि दोनों डाउनवोटर्स को आपत्तिजनक क्या मिला।)
डेडुप्लिकेटर

ओओपी के लिए विरासत की आवश्यकता नहीं है, इसलिए दो संरचनाओं के बीच संगतता व्यवहार में कोई समस्या नहीं है। मैं फंक्शन पॉइंटर्स को एक संरचना में डालकर, और उन कार्यों को एक विशेष तरीके से लागू करके वास्तविक OO प्राप्त कर सकता हूं। बेशक, foo_function(foo*, ...)C में यह छद्म OO केवल एक विशेष एपीआई शैली है जो कक्षाओं की तरह दिखने के लिए होता है, लेकिन इसे अधिक सटीक रूप से अमूर्त डेटा प्रकारों के साथ मॉड्यूलर प्रोग्रामिंग कहा जाना चाहिए।
आमोन

@Deduplicator: संकेतित उदाहरण देखें। फ़ील्ड "i1" दोनों संरचनाओं के सामान्य प्रारंभिक अनुक्रम का एक सदस्य है, लेकिन आधुनिक कंपाइलर विफल हो जाएगा यदि कोड "संरचना तिकड़ी" के प्रारंभिक सदस्य तक पहुंचने के लिए "संरचना युग्म" का उपयोग करने की कोशिश करता है।
सुपरकैट

कौन सा आधुनिक सी संकलक उस उदाहरण को विफल करता है? किसी भी दिलचस्प विकल्प की जरूरत है?
डेडुप्लिकेटर

-3

अगला कदम संरचना घोषणा को छिपाना है। आप इसे .h फ़ाइल में रखते हैं:

typedef struct foo_s foo_t;

foo_t * foo_new(...);
void foo_destroy(foo_t *foo);
some_type foo_whatever(foo_t *foo, ...);
...

और फिर .c फ़ाइल में:

struct foo_s {
    ...
};

6
यह उद्देश्य के आधार पर अगला कदम हो सकता है। यह है या नहीं, यह भी दूर से सवाल का जवाब देने की कोशिश नहीं करता है।
डेडुप्लिकेटर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.