Ş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::f
içeride bar.cpp
. Eğer uygulanmasını değiştirirseniz, ancak Foo::f
, her iki bar.cpp
ve qux.cpp
ihtiyaç derlenmesi gerekecektir. Her Foo::f
iki dosyada da hayatın uygulanması, hiçbir parçası Qux
doğ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
Foo
sınıfa üye eklerken , başlığa Bar
eklemeniz gerekir foo.h
. Foo::f
Bir .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::f
yeniden 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.tpp
iç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.cpp
uygulamasını değiştirirseniz yeniden derlenmesi gerekir Foo::f
. Dosyanın qux.cpp
yeniden 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.