मैं C ++ में फ़ंक्शन के भीतर संरचनाओं और कक्षाओं को क्यों परिभाषित कर सकता हूं?


91

मैंने गलती से सी ++ में ऐसा कुछ किया था, और यह काम करता है। मैं ऐसा क्यों कर सकता हूं?

int main(int argc, char** argv) {
    struct MyStruct
    {
      int somevalue;
    };

    MyStruct s;
    s.somevalue = 5;
}

अब ऐसा करने के बाद, मुझे कुछ समय पहले इस ट्रिक के बारे में याद आया, सी ++ के लिए एक तरह के गरीब-आदमी के कार्यात्मक प्रोग्रामिंग टूल के रूप में, लेकिन मुझे याद नहीं है कि यह क्यों मान्य है, या मैंने इसे कहां पढ़ा है।

या तो सवाल का जवाब स्वागत है!

नोट: हालांकि प्रश्न लिखते समय मुझे इस प्रश्न का कोई संदर्भ नहीं मिला , वर्तमान साइड-बार इसे इंगित करता है इसलिए मैं इसे यहाँ संदर्भ के लिए रखूँगा, या तो प्रश्न अलग है लेकिन उपयोगी हो सकता है।


जवाबों:


71

[EDIT 18/4/2013]: खुशी से, नीचे उल्लिखित प्रतिबंध को C ++ 11 में हटा दिया गया है, इसलिए स्थानीय स्तर पर परिभाषित कक्षाएं सभी के बाद उपयोगी हैं! टिप्पणी करने के लिए धन्यवाद बांस।

स्थानीय रूप से वर्गों को परिभाषित करने की क्षमता होगी कस्टम functors (एक साथ कक्षाएं बनाने बनाने operator()()जैसे तुलना काम करता है, को पारित करने के लिए std::sort()या "पाश शरीर" के साथ प्रयोग की जाने वाली std::for_each()) अधिक सुविधाजनक।

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

[EDIT 1/11/2009]

मानक से प्रासंगिक उद्धरण है:

14.3.1 / 2:। एक स्थानीय प्रकार, बिना लिंकेज वाला एक प्रकार, एक अनाम प्रकार या इनमें से किसी भी प्रकार से कंपाउंड किया गया टेम्पलेट टेम्पलेट-पैरामीटर के लिए टेम्पलेट-तर्क के रूप में उपयोग नहीं किया जाएगा।


2
हालांकि आनुभविक रूप से, यह MSVC ++ 8 के साथ काम करता है। (लेकिन जी ++ के साथ नहीं।)
j_random_hacker

Im 4.3.3 gcc का उपयोग कर रहा है, और यह वहाँ काम करने लगता है: pastebin.com/f65b876b । क्या आपके पास एक संदर्भ है जहां मानक इसे मना करता है? यह मुझे लगता है कि उपयोग के समय आसानी से तुरंत हो सकता है।
20

@ कैट्सकुल: 14.3.1 / 2: "एक स्थानीय प्रकार, बिना लिंकेज वाला एक प्रकार, एक अनाम प्रकार या इनमें से किसी भी प्रकार से कंपाउंड किया गया टेम्पलेट टेम्पलेट-पैरामीटर के लिए टेम्पलेट-तर्क के रूप में उपयोग नहीं किया जाएगा"। मेरा अनुमान है कि स्थानीय वर्गों को अभी तक सूचना के एक और समूह की आवश्यकता होगी, जिसे नामांकित नामों में धकेल दिया जाए, लेकिन मुझे यह पता नहीं है। बेशक एक विशेष संकलक इसके चारों ओर पाने के लिए एक्सटेंशन की पेशकश कर सकता है, क्योंकि ऐसा लगता है कि MSVC ++ 8 और हाल के संस्करण g ++ करते हैं।
j_random_hacker 6

9
यह प्रतिबंध C ++ 11 में हटा लिया गया था।
स्टीफन डॉलबर्ग

31

फ़ैक्टरी डिज़ाइन पैटर्न में स्थानीय रूप से परिभाषित C ++ कक्षाओं का एक अनुप्रयोग है :


// In some header
class Base
{
public:
    virtual ~Base() {}
    virtual void DoStuff() = 0;
};

Base* CreateBase( const Param& );

// in some .cpp file
Base* CreateBase( const Params& p )
{
    struct Impl: Base
    {
        virtual void DoStuff() { ... }
    };

    ...
    return new Impl;
}

हालांकि आप अनाम नाम स्थान के साथ भी ऐसा कर सकते हैं।


दिलचस्प! हालाँकि, मेरे द्वारा उल्लिखित टेम्पलेट्स के संबंध में प्रतिबंध लागू होंगे, लेकिन यह दृष्टिकोण गारंटी देता है कि CreateBase () को छोड़कर Impl के उदाहरण नहीं बनाए जा सकते (या यहां तक ​​कि बात की गई है!)। तो यह एक शानदार तरीका लगता है कि ग्राहक किस हद तक कार्यान्वयन विवरण पर निर्भर करते हैं। +1।
j_random_hacker

26
यह एक साफ विचार है, निश्चित नहीं है कि मैं इसे जल्द ही किसी भी समय उपयोग करूंगा, लेकिन शायद कुछ चूजों को प्रभावित करने के लिए बार में बाहर खींचने के लिए एक अच्छा :) :)
रॉबर्ट गोल्ड

2
(मैं चिक्स BTW के बारे में बात कर रहा हूं, जवाब नहीं!)
markh44

9
lol रॉबर्ट ... हाँ, कुछ भी नहीं एक महिला को प्रभावित करता है जैसे C ++ के अस्पष्ट कोनों के बारे में जानना ...
j_random_hacker

10

यह कुछ स्टैक-आधारित अपवाद-सुरक्षा कार्य करने के लिए वास्तव में बहुत उपयोगी है। या कई वापसी बिंदुओं के साथ एक फ़ंक्शन से सामान्य सफाई। इसे अक्सर RAII (संसाधन अधिग्रहण इनिशियेशन) मुहावरा कहा जाता है।

void function()
{

    struct Cleaner
    {
        Cleaner()
        {
            // do some initialization code in here
            // maybe start some transaction, or acquire a mutex or something
        }

        ~Cleaner()
        {
             // do the associated cleanup
             // (commit your transaction, release your mutex, etc.)
        }
    };

    Cleaner cleaner;

    // Now do something really dangerous
    // But you know that even in the case of an uncaught exception, 
    // ~Cleaner will be called.

    // Or alternatively, write some ill-advised code with multiple return points here.
    // No matter where you return from the function ~Cleaner will be called.
}

5
Cleaner cleaner();मुझे लगता है कि यह एक वस्तु परिभाषा के बजाय कार्य घोषणा होगी।
उपयोगकर्ता

2
@user आप सही हैं। डिफ़ॉल्ट कंस्ट्रक्टर को कॉल करने के लिए, उसे लिखना चाहिए Cleaner cleaner;या Cleaner cleaner{};
callyalater

फ़ंक्शंस के अंदर की कक्षाओं का RAII से कोई लेना-देना नहीं है और इसके अलावा, यह एक मान्य C ++ कोड नहीं है और इसे संकलित नहीं करेगा।
मिखाइल वासिलीव

1
यहां तक ​​कि कार्यों के अंदर भी, इस तरह की कक्षाएं सटीक हैं कि C ++ में RAII क्या है।
क्रिस्टोफर ब्रून्स

9

खैर, मूल रूप से, क्यों नहीं? structC में A (समय के समय में वापस जाना) एक रिकॉर्ड संरचना घोषित करने का एक तरीका था। यदि आप एक चाहते हैं, तो इसे घोषित करने में सक्षम क्यों नहीं हैं जहां आप एक साधारण चर घोषित करेंगे?

एक बार जब आप ऐसा कर लेते हैं, तो याद रखें कि C ++ का एक लक्ष्य सी के साथ संगत होना था यदि संभव हो तो। इसलिए यह रुकी रही।


एक साफ सुथरी सुविधा बची है, लेकिन j_random_hacker ने बताया कि यह उतना उपयोगी नहीं है जितना मैं C ++ में कल्पना कर रहा था: /
रॉबर्ट गोल्ड

हाँ, सी में भी स्कूपिंग नियम अजीब थे। मुझे लगता है, अब जब मैंने सी + + के साथ 25+ साल का अनुभव किया है, तो शायद सी की तरह ही रहने का प्रयास करता हूं क्योंकि उन्होंने गलती की होगी। दूसरी ओर, एफिल जैसी अधिक सुरुचिपूर्ण भाषाओं को लगभग आसानी से नहीं अपनाया गया।
चार्ली मार्टिन

हां, मैंने एक मौजूदा C कोडबेस को C ++ (लेकिन एफिल के लिए नहीं) में माइग्रेट किया है।
क्रिस डब्ल्यू

5

इसका उल्लेख है, उदाहरण के लिए, अनुभाग "7.8: स्थानीय कक्षाएं: कार्य के अंदर कक्षाएं" http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html जो इसे "स्थानीय वर्ग" कहता है और इसे "कहते हैं" वंशानुक्रम या टेम्पलेट्स से जुड़े उन्नत अनुप्रयोगों में बहुत उपयोगी हो सकता है "।


3

यह ऑब्जेक्ट्स के सरणियों को बनाने के लिए है जो ठीक से आरंभीकृत हैं।

मेरे पास एक वर्ग सी है जिसका कोई डिफ़ॉल्ट निर्माता नहीं है। मुझे कक्षा सी की वस्तुओं की एक सरणी चाहिए। मुझे पता है कि मैं उन वस्तुओं को कैसे शुरू करना चाहता हूं, फिर एक वर्ग डी को सी से एक स्थिर विधि से प्राप्त करें जो डी के डिफ़ॉल्ट निर्माणकर्ता में सी के लिए तर्क प्रदान करता है:

#include <iostream>
using namespace std;

class C {
public:
  C(int x) : mData(x)  {}
  int method() { return mData; }
  // ...
private:
  int mData;
};

void f() {

  // Here I am in f.  I need an array of 50 C objects starting with C(22)

  class D : public C {
  public:
    D() : C(D::clicker()) {}
  private:
    // I want my C objects to be initialized with consecutive
    // integers, starting at 22.
    static int clicker() { 
      static int current = 22;
      return current++;
    } 
  };

  D array[50] ;

  // Now I will display the object in position 11 to verify it got initialized
  // with the right value.  

  cout << "This should be 33: --> " << array[11].method() << endl;

  cout << "sizodf(C): " << sizeof(C) << endl;
  cout << "sizeof(D): " << sizeof(D) << endl;

  return;

}

int main(int, char **) {
  f();
  return 0;
}

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


निश्चित रूप से एक दिलचस्प अनुप्रयोग! यह सुनिश्चित नहीं है कि यह बुद्धिमान या सुरक्षित है - यदि आपको सी के एक सरणी के रूप में डी की उस सरणी का इलाज करने की आवश्यकता है (जैसे आपको D*पैरामीटर लेने वाले फ़ंक्शन में इसे पास करने की आवश्यकता है ) तो यह चुपचाप टूट जाएगा यदि डी वास्तव में सी से बड़ा है। । (मुझे लगता है ...)
j_random_hacker

+ j_random_hacker, sizeof (D) == sizeof (C)। मैंने आपके लिए एक साइज़ोफ़ () रिपोर्ट जोड़ी।
थॉमस एल होलाडे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.