एक अस्थायी C ++ वर्ग लिखते समय, आपके पास आमतौर पर तीन विकल्प होते हैं:
(1) हेडर में घोषणा और परिभाषा रखें।
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f()
{
...
}
};
या
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f();
};
template <typename T>
inline void Foo::f()
{
...
}
समर्थक:
- बहुत सुविधाजनक उपयोग (बस हेडर शामिल करें)।
कोन:
- इंटरफ़ेस और विधि कार्यान्वयन मिश्रित हैं। यह "सिर्फ" एक पठनीयता समस्या है। कुछ को यह अप्राप्य लगता है, क्योंकि यह सामान्य से अलग है ।h / .cpp दृष्टिकोण। हालाँकि, ध्यान रखें कि यह अन्य भाषाओं में कोई समस्या नहीं है, उदाहरण के लिए, C # और Java।
- उच्च पुनर्निर्माण प्रभाव: यदि आप
Foo
सदस्य के रूप में एक नए वर्ग की घोषणा करते हैं , तो आपको शामिल करने की आवश्यकता है foo.h
। इसका अर्थ है कि Foo::f
हेडर और स्रोत फ़ाइलों दोनों के माध्यम से प्रचार के कार्यान्वयन को बदलना ।
पुनर्निर्माण प्रभाव पर एक करीब से नज़र डालते हैं: गैर-अस्थायी सी ++ कक्षाओं के लिए, आप .hpp में .h और विधि परिभाषाएँ घोषित करते हैं। इस तरह, जब किसी पद्धति के कार्यान्वयन को बदल दिया जाता है, केवल एक .cpp को फिर से शुरू करने की आवश्यकता होती है। यह टेम्पलेट कक्षाओं के लिए अलग है। यदि आपके पास सभी कोड हैं। निम्नलिखित उदाहरण पर एक नज़र डालें:
// bar.h
#pragma once
#include "foo.h"
struct Bar
{
void b();
Foo<int> foo;
};
// bar.cpp
#include "bar.h"
void Bar::b()
{
foo.f();
}
// qux.h
#pragma once
#include "bar.h"
struct Qux
{
void q();
Bar bar;
}
// qux.cpp
#include "qux.h"
void Qux::q()
{
bar.b();
}
यहां, केवल उपयोग Foo::f
अंदर है bar.cpp
। हालांकि, अगर आप के कार्यान्वयन को बदलने Foo::f
, दोनों bar.cpp
और qux.cpp
जरूरत कंपाइल किया जाना है। Foo::f
दोनों फ़ाइलों में जीवन का कार्यान्वयन , भले ही Qux
सीधे किसी भी हिस्से का उपयोग नहीं करता है Foo::f
। बड़ी परियोजनाओं के लिए, यह जल्द ही एक समस्या बन सकती है।
(2) .h में घोषणापत्र डालें और .tpp में परिभाषा करें और इसे .h में शामिल करें।
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f();
};
#include "foo.tpp"
// foo.tpp
#pragma once // not necessary if foo.h is the only one that includes this file
template <typename T>
inline void Foo::f()
{
...
}
समर्थक:
- बहुत सुविधाजनक उपयोग (बस हेडर शामिल करें)।
- इंटरफ़ेस और विधि की परिभाषाएँ अलग हो गई हैं।
कोन:
- उच्च पुनर्निर्माण प्रभाव (उसी के रूप में (1) )।
यह समाधान .h / .cpp की तरह ही दो अलग-अलग फाइलों में घोषणा और विधि की परिभाषा को अलग करता है। हालांकि, इस दृष्टिकोण में (1) के रूप में एक ही पुनर्निर्माण समस्या है , क्योंकि हेडर में सीधे विधि परिभाषाएं शामिल हैं।
(3) .hpp में .h और परिभाषा में घोषणा डालें, लेकिन .hpp में .hpp को शामिल न करें।
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f();
};
// foo.tpp
#pragma once
template <typename T>
void Foo::f()
{
...
}
समर्थक:
- .H / .cpp जुदाई के रूप में प्रभाव को कम करता है।
- इंटरफ़ेस और विधि की परिभाषाएँ अलग हो गई हैं।
कोन:
- असुविधाजनक उपयोग:
Foo
एक वर्ग में एक सदस्य को जोड़ते समय Bar
, आपको foo.h
हेडर में शामिल करने की आवश्यकता होती है । यदि आप Foo::f
एक .cpp में कॉल करते हैं , तो आपको वहां भी शामिल करना foo.tpp
होगा।
यह दृष्टिकोण पुनर्निर्माण प्रभाव को कम करता है, केवल .cpp फ़ाइलों के लिए जो वास्तव में उपयोग किए जाने की Foo::f
आवश्यकता होती है उन्हें पुन: कंपाइल किया जाता है। हालांकि, यह एक मूल्य पर आता है: उन सभी फ़ाइलों को शामिल करने की आवश्यकता है foo.tpp
। ऊपर से उदाहरण लें और नए दृष्टिकोण का उपयोग करें:
// bar.h
#pragma once
#include "foo.h"
struct Bar
{
void b();
Foo<int> foo;
};
// bar.cpp
#include "bar.h"
#include "foo.tpp"
void Bar::b()
{
foo.f();
}
// qux.h
#pragma once
#include "bar.h"
struct Qux
{
void q();
Bar bar;
}
// qux.cpp
#include "qux.h"
void Qux::q()
{
bar.b();
}
जैसा कि आप देख सकते हैं, एकमात्र अंतर इसमें शामिल अतिरिक्त foo.tpp
है bar.cpp
। यह असुविधाजनक है और एक वर्ग के लिए एक दूसरे को शामिल करना इस बात पर निर्भर करता है कि क्या आप उस पर कॉल करने के तरीके बहुत बदसूरत लगते हैं। हालांकि, आप पुनर्निर्माण प्रभाव को कम करते हैं: bar.cpp
यदि आप के कार्यान्वयन को बदलते हैं, तो केवल पुनर्नवीनीकरण की आवश्यकता है Foo::f
। फ़ाइल qux.cpp
को कोई पुनर्संयोजन की आवश्यकता है।
सारांश:
यदि आप एक पुस्तकालय लागू करते हैं, तो आपको आमतौर पर प्रभाव के पुनर्निर्माण के बारे में परवाह करने की आवश्यकता नहीं है। आपकी लाइब्रेरी के उपयोगकर्ता किसी रिलीज़ को हड़प लेते हैं और उसका उपयोग करते हैं और लाइब्रेरी कार्यान्वयन उपयोगकर्ता के दिन-प्रतिदिन के काम में नहीं बदलता है। ऐसे मामलों में, पुस्तकालय दृष्टिकोण (1) या (2) का उपयोग कर सकता है और यह केवल स्वाद का मामला है जिसे आप चुनते हैं।
हालाँकि, यदि आप किसी एप्लिकेशन पर काम कर रहे हैं, या यदि आप अपनी कंपनी की आंतरिक लाइब्रेरी पर काम कर रहे हैं, तो कोड अक्सर बदलता रहता है। इसलिए आपको प्रभाव के पुनर्निर्माण के बारे में ध्यान रखना होगा। यदि आप अपने डेवलपर्स को अतिरिक्त शामिल करने के लिए स्वीकार करते हैं, तो दृष्टिकोण (3) चुनना एक अच्छा विकल्प हो सकता है।