Şablonlu bir C ++ sınıfı yazarken, genellikle üç seçeneğiniz vardır:
(1) Başlığa açıklama ve tanım koyun.
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f()
{
...
}
};
veya
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f();
};
template <typename T>
inline void Foo::f()
{
...
}
Pro:
- Çok rahat kullanım (sadece başlığı dahil edin).
con:
- Arayüz ve yöntem uygulaması karışıktır. Bu sadece okunabilirlik problemidir. Bazıları bunu sürdürülemez bulur, çünkü normal .h / .cpp yaklaşımından farklıdır. Ancak, bunun diğer dillerde, örneğin C # ve Java'da sorun olmadığını unutmayın.
- Yüksek yeniden oluşturma etkisi:
FooÜye olarak yeni bir sınıf bildirirseniz , eklemeniz gerekir foo.h. Bu Foo::f, hem üstbilgi hem de kaynak dosyaları aracılığıyla yayılımların uygulanmasının değiştirildiği anlamına gelir .
Yeniden oluşturma etkisine daha yakından bakalım: Şablon olmayan C ++ sınıfları için, .h ve .cpp yöntem tanımlarını koyarsınız. Bu şekilde, bir yöntemin uygulanması değiştirildiğinde, yalnızca bir .cpp dosyasının yeniden derlenmesi gerekir. Bu, .h tüm kodlarınızı içeriyorsa şablon sınıfları için farklıdır. Aşağıdaki örneğe bir göz atın:
// 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();
}
Burada, tek kullanımı Foo::fiçeride bar.cpp. Eğer uygulanmasını değiştirirseniz, ancak Foo::f, her iki bar.cppve qux.cppihtiyaç derlenmesi gerekecektir. Her Foo::fiki dosyada da hayatın uygulanması, hiçbir parçası Quxdoğrudan bir şey kullanmasa da Foo::f. Büyük projeler için, bu yakında bir sorun haline gelebilir.
(2) Beyanı .h'ye ve tanımı .tpp'ye koyun ve .h'ye ekleyin.
// 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()
{
...
}
Pro:
- Çok rahat kullanım (sadece başlığı dahil edin).
- Arayüz ve yöntem tanımları ayrılır.
con:
- Yüksek yeniden inşaat etkisi ( (1) ile aynı ).
Bu çözüm, bildirim ve yöntem tanımını, .h / .cpp gibi iki ayrı dosyada ayırır. Ancak, bu yaklaşım (1) ile aynı yeniden oluşturma sorununa sahiptir , çünkü üstbilgi doğrudan yöntem tanımlarını içerir.
(3) .hpp ve .tpp içindeki tanımları belirtin, ancak .h.
// foo.h
#pragma once
template <typename T>
struct Foo
{
void f();
};
// foo.tpp
#pragma once
template <typename T>
void Foo::f()
{
...
}
Pro:
- .H / .cpp ayrımı gibi yeniden oluşturma etkisini azaltır.
- Arayüz ve yöntem tanımları ayrılır.
con:
- Uygunsuz kullanım: Bir
Foosınıfa üye eklerken , başlığa Bareklemeniz gerekir foo.h. Foo::fBir .cpp çağırırsanız , oraya da eklemeniz gerekir foo.tpp.
Bu yaklaşım yeniden oluşturma etkisini azaltır, çünkü yalnızca gerçekten kullanılan .cpp dosyalarının Foo::fyeniden derlenmesi gerekir. Ancak, bunun bir bedeli vardır: Tüm bu dosyaların dahil edilmesi gerekir foo.tpp. Yukarıdaki örneği alın ve yeni yaklaşımı kullanın:
// 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();
}
Gördüğünüz gibi, tek fark, ek bir include foo.tppiçinde bar.cpp. Bu elverişsizdir ve üzerinde yöntem çağırıp çağırmamanıza bağlı olarak sınıf için ikinci bir ekleme eklemek çok çirkin gözükür. Ancak, yeniden oluşturma etkisini azaltırsınız: Yalnızca bar.cppuygulamasını değiştirirseniz yeniden derlenmesi gerekir Foo::f. Dosyanın qux.cppyeniden derlenmesi gerekmez.
Özet:
Bir kitaplık uygularsanız, genellikle yeniden oluşturma etkisini önemsemeniz gerekmez. Kitaplığınızın kullanıcıları bir sürüm alır ve kullanır ve kitaplık uygulaması kullanıcının günlük işlerinde değişmez. Bu gibi durumlarda, kütüphane (1) veya (2) yaklaşımını kullanabilir ve hangisini seçtiğiniz bir zevk meselesidir.
Ancak, bir uygulama üzerinde çalışıyorsanız veya şirketinizin dahili bir kitaplığı üzerinde çalışıyorsanız, kod sık sık değişir. Bu yüzden yeniden inşa etmeyi önemsemelisiniz. Geliştiricilerinizin ek kapsamı kabul etmesini sağlarsanız, yaklaşım (3) seçimi iyi bir seçenek olabilir.