Başlık dosyalarına C ++ tanımları yerleştirmek iyi bir uygulama mı?


196

C ++ ile kişisel tarzım her zaman sınıf bildirimlerini bir içerme dosyasına ve tanımları bir .cpp dosyasına koymak zorundadır, tıpkı Loki'nin C ++ Başlık Dosyaları, Kod Ayrımı'na verdiği yanıtta belirtildiği gibi . Kuşkusuz, bu stili sevmem nedeninin bir kısmı, her ikisi de şartname dosyaları ve vücut dosyalarıyla benzer bir şemaya sahip olan Modula-2 ve Ada'yı kodladığım tüm yıllar ile ilgili.

Ben tüm C ++ bildirimleri, mümkünse, orada başlık dosyasında tanımları içermesi gerektiğini ısrar eden C ++, ben daha çok daha bilgili, bir iş arkadaşım var. Bunun geçerli bir alternatif stil, hatta biraz daha iyi bir stil olduğunu söylemiyor, aksine bu herkesin artık C ++ için kullandığı evrensel olarak kabul edilen yeni stil.

Eskiden olduğum kadar esnek değilim, bu yüzden onunla birlikte birkaç kişi daha görene kadar onun bu bandwagonuna tırmandığım için gerçekten endişeli değilim. Peki bu deyim gerçekten ne kadar yaygın?

Sadece cevaplara bir yapı kazandırmak için: Şimdi Yol çok yaygın mı, biraz yaygın mı, nadir mi yoksa hata mı deli?


başlıktaki tek satırlık işlevler (alıcılar ve ayarlayıcılar) yaygındır. Kısa bir ikinci bakış daha uzun. Belki de sadece aynı başlıkta başka biri tarafından kullanılan küçük bir sınıfın tam tanımı için?
Martin Beckett

Ben her zaman şimdiye kadar tüm sınıf tanımları başlıkları koydum. sadece pimpl sınıflarının tanımları istisnadır. Sadece başlıklarda beyan ederim.
Johannes Schaub - litb

3
Belki de Visual C ++ bu kodun yazılmasında ısrar ettiği için yol olduğunu düşünüyor. Bir düğmeyi tıklattığınızda, uygulama başlık dosyasında oluşturulur. Diğerlerinin aşağıda açıkladığı nedenlerle Microsoft'un bunu neden teşvik edeceğini bilmiyorum.
WKS

4
Microsoft, herkes C # programlamak istiyorum ve C #, "üstbilgi" vs "gövde" ayrım yoktur, sadece bir dosyadır. Hem C ++ hem de C # dünyasında uzun zamandır bulunmakta olan C # yolunun üstesinden gelmek çok daha kolay.
Mark Lakata

1
@MarkLakata - Bu gerçekten işaret ettiği şeylerden biri. Bu argümanı son zamanlarda duymadım, ama IIRC, Java ve C #'ın bu şekilde çalıştığını savunuyordu ve C # o zamanlar yepyeni oldu, bu da tüm dillerin yakında takip edeceği bir trend haline geldi
TED

Yanıtlar:


209

İş arkadaşınız yanlış, ortak yol her zaman kodu .cpp dosyalarına (veya istediğiniz herhangi bir uzantıya) ve başlıklara bildirimler koymaktır.

Bazen üstbilgiye kod koymak için bazı değer vardır, bu derleyici tarafından daha akıllı satır içi izin verebilir. Ancak aynı zamanda, derleyici zamanlarınızı yok edebilir, çünkü tüm kodlar derleyici tarafından her eklendiğinde işlenmelidir.

Son olarak, tüm kod üstbilgi olduğunda dairesel nesne ilişkilerine (bazen istenir) sahip olmak genellikle can sıkıcı bir durumdur.

Sonuç olarak, haklıydınız, o yanlış.

EDIT: Sorunu düşünüyorum. Söylediklerinin doğru olduğu bir durum var . şablonları. Boost gibi daha yeni birçok "modern" kütüphane, şablonları yoğun şekilde kullanır ve genellikle "yalnızca başlık" tır. Ancak, bu sadece şablonlarla uğraşırken yapılmalıdır çünkü onlarla uğraşırken bunu yapmanın tek yolu budur.

DÜZENLEME: Bazı insanlar biraz daha fazla açıklama ister, burada "yalnızca başlık" kodu yazmanın olumsuz yönleri hakkında bazı düşünceler:

Etrafta arama yaparsanız, pek çok insanın destekle uğraşırken derleme sürelerini azaltmanın bir yolunu bulmaya çalıştığını göreceksiniz. Örneğin: Boost Asio ile derleme sürelerini kısaltmak, Boost Asio , tek bir 1K dosyasının derlemesini derleme ile birlikte görüyor. 14'ler "patlayan" gibi görünmeyebilir, ancak kesinlikle tipik olandan çok daha uzundur ve oldukça hızlı bir şekilde toplanabilir. Büyük bir projeyle uğraşırken. Yalnızca başlık kitaplıkları derleme zamanlarını oldukça ölçülebilir bir şekilde etkiler. Sadece tolere ediyoruz çünkü destek çok faydalı.

Ek olarak, yalnızca başlıklarda yapılamayan birçok şey vardır (destek, iş parçacıkları, dosya sistemi vb. Gibi belirli parçalar için bağlamanız gereken kütüphanelere bile sahiptir). Birincil örnek, birden çok tanım hatasıyla karşılaşacağınız için yalnızca üstbilgi kütüphanelerinde basit tek nesnelerin bulunmamasıdır (tekton olan iğrençliğe başvurmadıkça). NOT: C ++ 17'nin satır içi değişkenleri bu özel örneği gelecekte yapılabilir hale getirecektir.

Son nokta olarak, yalnızca başlık kodunun bir örneği olarak boost kullanıldığında, büyük bir ayrıntı genellikle kaçırılır.

Boost, kullanıcı seviyesi kodu değil, kitaplıktır. bu yüzden sık sık değişmez. Kullanıcı kodunda, her şeyi başlıklara koyarsanız, her küçük değişiklik tüm projeyi yeniden derlemenize neden olur. Bu anıtsal bir zaman kaybıdır (ve derlemeden derlemeye geçiş yapmayan kütüphaneler için geçerli değildir). Üstbilgi / kaynak ve daha iyisi arasında bir şeyler bölüştüğünüzde, içerilen içeriği azaltmak için ileri bildirimler kullanın, bir gün içinde toplandığında saatlerce yeniden derleme kaydedebilirsiniz.


16
Eminim onu ​​buradan alıyor. Bu ortaya çıktığında şablonlar getirir. Onun argümanı kabaca bütün kodları tutarlılık için bu şekilde yapmanız gerektiğidir.
TED

14
Yaptığı kötü bir argüman, silahlarına sadık
kal

11
"Dışa aktar" anahtar kelimesi destekleniyorsa şablon tanımları CPP dosyalarında olabilir. Bu, çoğu derleme tarafından bile benim bilgim dahilinde uygulanmayan C ++ 'ın karanlık bir köşesidir.
Andrei Taranchenko

2
Bir örnek için bu cevabın alt kısmına bakınız (üst kısım biraz kıvrılmıştır): stackoverflow.com/questions/555330/…
Evan Teran

3
"Yaşasın, bağlayıcı hatası yok" adresindeki bu tartışma için anlamlı olmaya başlar.
Evan Teran

158

C ++ kodlayıcılarının Yol üzerinde anlaştığı gün , kuzular aslanlarla uzanacak, Filistinliler İsraillileri kucaklayacak ve kedilerin ve köpeklerin evlenmesine izin verilecek.

Bu noktada .h ve .cpp dosyaları arasındaki ayrım çoğunlukla geçicidir, derleyici optimizasyonlarının uzun bir geçmişi vardır. Benim açımdan, açıklamalar başlığa, tanımlar ise uygulama dosyasına aittir. Ama bu sadece alışkanlık, din değil.


140
"C ++ kodlayıcıları The Way üzerinde anlaştılar ..." sadece bir C ++ kodlayıcı kaldı!
Brian Ensink

9
Zaten
.hpp

6
Hepimiz körüz ve C ++ bir fil.
Roderick Taylor

alışkanlık? kapsam tanımlamak için .h kullanmaya ne dersiniz? hangi şeyle değiştirildi?
Hernán Eche

28

Üstbilgilerdeki kod, bildirimler yerine gerçek kodu değiştirdiğinizde üstbilgiyi içeren tüm dosyaların yeniden derlenmesini zorladığı için genellikle kötü bir fikirdir. Ayrıca, başlığı içeren her dosyadaki kodu ayrıştırmanız gerektiğinden derlemeyi de yavaşlatır.

Başlık dosyalarında kod bulundurmanın bir nedeni, genellikle satır içi anahtar kelimenin düzgün çalışması ve diğer cpp dosyalarında örneklenen şablonları kullanırken gerekli olmasıdır.


1
"Bu açıklamaları yerine gerçek kodu değiştirdiğinizde başlık içeren tüm dosyaların yeniden derleme zorlar" Bence bu en gerçek nedeni; Ayrıca, başlıklardaki bildirimlerin .c dosyalarındaki uygulamadan daha az değiştiği gerçeğiyle de ilgilidir.
Ninad

20

İş arkadaşınızı bilgilendiren şey, çoğu C ++ kodunun maksimum kullanılabilirliğe izin verecek şekilde ayarlanması gerektiğidir. Ve şablon ayarlanmışsa, her şeyin bir başlık dosyasında olması gerekir, böylece istemci kodu onu görebilir ve somutlaştırabilir. Boost ve STL için yeterince iyiyse, bizim için yeterince iyidir.

Bu bakış açısına katılmıyorum, ama geldiği yer bu olabilir.


Bence bu konuda haklısın. Tartıştığımızda, her zaman az çok bunu yapmak zorunda olduğunuz şablon örneğini kullanıyor . Ben de "zorundayım" ile aynı fikirde değilim, ama benim alternatifler oldukça kıvrık.
TED

1
@ted - şablonlu kod için uygulamayı başlığa koymanız gerekir. 'Dışa aktar' anahtar kelimesi, bir derleyicinin şablonların tanımını ve tanımını ayırmasını desteklemesine izin verir, ancak dışa aktarma desteği oldukça mevcut değildir. anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf
Michael Burr

Bir başlık, evet, ancak aynı başlık olması gerekmez. Bilinmeyenin cevabını aşağıya bakın.
TED

Bu mantıklı, ama daha önce bu stile rastladığımı söyleyemem.
Michael Burr

14

Bence iş arkadaşınız akıllı ve siz de haklısınız.

Her şeyi başlıklara koymanın yararlı olduğunu düşündüğüm şeyler:

  1. Başlıkları ve kaynakları yazmaya ve senkronize etmeye gerek yoktur.

  2. Yapı düzdür ve dairesel bağımlılıklar kodlayıcıyı "daha iyi" bir yapı yapmaya zorlamaz.

  3. Taşınabilir, yeni bir projeye gömülmesi kolaydır.

Derleme zamanı problemine katılıyorum, ancak şunu fark etmeliyiz:

  1. Kaynak dosya değişikliğinin büyük olasılıkla tüm projenin yeniden derlenmesine yol açan başlık dosyalarını değiştirmesi muhtemeldir.

  2. Derleme hızı eskisinden çok daha hızlı. Ve uzun süre ve yüksek sıklıkta inşa edilecek bir projeniz varsa, proje tasarımınızın kusurları olduğunu gösterebilir. Görevleri farklı projelere ayırın ve modül bu sorunu önleyebilir.

Son olarak, sadece kişisel görüşüme göre iş arkadaşınızı desteklemek istiyorum.


3
+1. Bir başlıkta sadece uzun derleme süreleri projesinin kötü tasarım olan çok fazla bağımlılığa işaret edebileceği fikri yoktu. İyi bir nokta! Ancak bu bağımlılıklar, derleme süresinin gerçekten kısa olduğu ölçüde kaldırılabilir mi?
TobiMcNamobi

@TobiMcNamobi: Kötü tasarım kararları hakkında daha iyi geri bildirim almak için "yavaşlama" fikrini seviyorum. Bununla birlikte, sadece başlık ile ayrı ayrı derlenmiş durumda, bu fikre karar verirsek, tek bir derleme birimi ve büyük derleme süreleri ile sonuçlanırız. Tasarım gerçekten harika olsa bile.
Jo So

Başka bir deyişle, arayüz ve uygulama arasındaki ayrım aslında tasarımınızın bir parçasıdır. C dilinde, kapsülleme ile ilgili kararlarınızı üstbilgi ve uygulamadaki ayırma yoluyla ifade etmeniz gerekir.
Jo So

1
Modern diller gibi başlıkları tamamen bırakmak için herhangi bir dezavantaj olup olmadığını merak etmeye başlıyorum.
Jo So

12

Çoğu zaman, önemsiz üye işlevlerini, üst üste gelmelerine izin vermek için başlık dosyasına koyacağım. Ancak tüm kod gövdesini oraya koymak, sadece şablonlarla tutarlı olmak için? Bu sade fındık.

Unutmayın: Aptalca bir tutarlılık, küçük zihinlerin hobgoblinidir .


Evet, ben de yaparım. Kullandığım genel kural "tek bir kod satırına uyuyorsa, üstbilgide bırak" satırında bir şey gibi görünüyor.
TED

Bir kitaplık bir şablon sınıfının gövdesini A<B>bir cpp dosyasında sağladığında ve sonra kullanıcı bir isterse ne olur A<C>?
jww

@jww Bunu açıkça belirtmedim, ancak şablon sınıfları başlıklarda tam olarak tanımlanmalı, böylece derleyici ihtiyacı olan her türle başlatabilir. Bu teknik bir gereksinim, stilistik bir seçim değil. Orijinal sorudaki sorun, birisinin şablonlar için iyi olup olmadığına karar vermesi, normal sınıflar için de iyi olduğunu düşünüyorum.
Mark Ransom

7

Tuomas'ın dediği gibi, başlığınız minimum olmalıdır. Tamamlamak için biraz genişleteceğim.

C++Projelerimde kişisel olarak 4 tür dosya kullanıyorum :

  • Halka açık:
  • Yönlendirme başlığı: şablonlar vb. Olması durumunda, bu dosya başlıkta görünecek yönlendirme bildirimlerini alır.
  • Başlık: Bu dosya varsa yönlendirme başlığını içerir ve herkese açık olmasını istediğim her şeyi bildirir (ve sınıfları tanımlar ...)
  • Özel:
  • Özel üstbilgi: Bu dosya, uygulama için ayrılmış bir üstbilgidir, üstbilgiyi içerir ve yardımcı işlevleri / yapıları bildirir (örneğin Pimpl için veya tahminler için). Gerekmiyorsa atlayın.
  • Kaynak dosya: özel üstbilgiyi (veya özel üstbilgi yoksa üstbilgiyi) içerir ve her şeyi tanımlar (şablon olmayan ...)

Dahası, bunu başka bir kuralla birleştiriyorum: Beyan edebileceğiniz şeyleri tanımlamayın. Tabii ki orada makul (Pimpl her yerde kullanarak oldukça bir güçlük).

Bu #include, onlardan ne zaman kurtulursam, başlıklarımdaki bir yönerge üzerinde ileri bir beyanı tercih ettiğim anlamına gelir .

Son olarak, bir görünürlük kuralı kullanıyorum: Sembollerimin kapsamlarını mümkün olduğunca sınırlandırıyorum, böylece dış kapsamları kirletmeyecekler.

Tamamen koymak:

// example_fwd.hpp
// Here necessary to forward declare the template class,
// you don't want people to declare them in case you wish to add
// another template symbol (with a default) later on
class MyClass;
template <class T> class MyClassT;

// example.hpp
#include "project/example_fwd.hpp"

// Those can't really be skipped
#include <string>
#include <vector>

#include "project/pimpl.hpp"

// Those can be forward declared easily
#include "project/foo_fwd.hpp"

namespace project { class Bar; }

namespace project
{
  class MyClass
  {
  public:
    struct Color // Limiting scope of enum
    {
      enum type { Red, Orange, Green };
    };
    typedef Color::type Color_t;

  public:
    MyClass(); // because of pimpl, I need to define the constructor

  private:
    struct Impl;
    pimpl<Impl> mImpl; // I won't describe pimpl here :p
  };

  template <class T> class MyClassT: public MyClass {};
} // namespace project

// example_impl.hpp (not visible to clients)
#include "project/example.hpp"
#include "project/bar.hpp"

template <class T> void check(MyClass<T> const& c) { }

// example.cpp
#include "example_impl.hpp"

// MyClass definition

Durumunda sadece gerekli: cankurtaran burada zamanların en ileri başlık yararsız olmasıdır typedefya templateve böylece uygulama başlığıdır;)


6

Daha fazla eğlence eklemek .ippiçin, şablon uygulamasını içeren (içinde yer alan .hpp), .hpparayüzü içerirken dosya ekleyebilirsiniz .

Geçici kod dışında (projeye bağlı olarak, dosyaların çoğunluğu veya azınlığı olabilir) normal kod vardır ve burada bildirimleri ve tanımları ayırmak daha iyidir. Gerektiğinde ileri bildirimler de sağlayın - bunun derleme zamanı üzerinde etkisi olabilir.


Şablon tanımlarıyla da bunu yaptım (aynı uzantıyı kullandığımdan emin olmasam da ... bir süredir).
TED

5

Genel olarak, yeni bir sınıf yazarken, sınıftaki tüm kodu koyacağım, bunun için başka bir dosyaya bakmak zorunda değilim .. Her şey çalıştıktan sonra, cpp dosyasına yöntemlerin gövdesini kırıyorum , prototipleri hpp dosyasında bırakarak.


4

Bu yeni yol gerçekten Yol ise , projelerimizde farklı yönlere doğru çalışıyor olabiliriz.

Çünkü başlıklarda gereksiz tüm şeylerden kaçınmaya çalışıyoruz. Bu, başlık kademesinden kaçınmayı içerir. Üstbilgilerdeki kodun eklenmesi için başka bir üstbilgiye ihtiyaç duyacağı, başka bir üstbilgiye vb. Şablonları kullanmak zorunda kalırsak, şablon öğeleri içeren başlıkların çok fazla kullanılmasını önleriz.

Ayrıca uygulanabilir olduğunda "opak işaretçi" desenini kullanıyoruz .

Bu uygulamalarla akranlarımızın çoğundan daha hızlı inşalar yapabiliriz. Ve evet ... kod veya sınıf üyelerini değiştirmek büyük yeniden yapılanmalara neden olmaz.


4

Şahsen bunu başlık dosyalarında yapıyorum:

// class-declaration

// inline-method-declarations

Bir şeyleri hızlı bir şekilde aramak için bir acı bulduğum için sınıftaki yöntemlerin kodunu karıştırmayı sevmiyorum.

TÜM yöntemleri başlık dosyasına koymak olmaz. Derleyici (normalde) sanal yöntemleri sıraya koyamaz ve (büyük olasılıkla) sadece döngüsüz küçük yöntemleri sıraya koyar (tamamen derleyiciye bağlıdır).

Sınıftaki yöntemleri yapmak geçerlidir ... ama gerçek bir bakış açısından sevmiyorum. Yöntemleri başlığa koymak, mümkün olduğunda satır içine alınacakları anlamına gelir.


2

IMHO, SADECE şablonlar ve / veya metaprogramlama yapıyorsa hak ediyor. Üstbilgi dosyalarını yalnızca bildirimlerle sınırlandırmanızın birçok nedeni vardır. Onlar sadece ... başlıklar. Kod eklemek istiyorsanız, onu bir kütüphane olarak derleyip bağlarsınız.


2

Tüm uygulamayı sınıf tanımından çıkardım. Sınıf tanımından doxygen yorumlarını almak istiyorum.


1
Geç olduğunu biliyorum, ama downvoters (veya sempatizanlar) neden yorum yapmak umurumda? Bu benim için makul bir ifade gibi görünüyor. Doxygen kullanıyoruz ve sorun kesinlikle ortaya çıktı.
TED

2

TÜM işlev tanımlarınızı başlık dosyasına koymanın kesinlikle saçma olduğunu düşünüyorum. Neden? Çünkü başlık dosyası sınıfınıza PUBLIC arabirimi olarak kullanılır. "Kara kutunun" dışında.

Nasıl kullanılacağına başvurmak için bir sınıfa bakmanız gerektiğinde, başlık dosyasına bakmalısınız. Başlık dosyası yapabileceklerinin bir listesini vermelidir (her bir işlevin nasıl kullanılacağının ayrıntılarını açıklamak için yorumlanmıştır) ve üye değişkenlerin bir listesini içermelidir. Tek tek her bir fonksiyonun NASIL uygulanacağını İÇERMEMELİDİR, çünkü bu gereksiz bilgilerin bir tekne yüküdür ve sadece başlık dosyasını keser.


1

Bu gerçekten sistemin karmaşıklığına ve şirket içi sözleşmelere bağlı değil mi?

Şu anda inanılmaz derecede karmaşık bir sinir ağı simülatörü üzerinde çalışıyorum ve kullanmam beklenen kabul edilen stil:

Classname.h içindeki sınıf tanımları classnameCode.h içindeki
sınıf kodu classname.cpp içindeki
yürütülebilir kod

Bu, geliştirici tarafından oluşturulan temel sınıflardan kullanıcı tarafından oluşturulan simülasyonları böler ve bu durumda en iyi sonucu verir.

Ancak, insanların bunu bir grafik uygulamasında yaptığını görmek için şaşırırdım, ya da amacı olan başka bir uygulama kullanıcılara bir kod tabanı sağlamak değildir.


1
"Sınıf kodu" ile "Yürütülebilir kod" arasındaki fark tam olarak nedir?
TED

Dediğim gibi, bu sinirsel bir simülatör: Kullanıcı nöron gibi hareket eden çok sayıda sınıf üzerine inşa edilmiş yürütülebilir simülasyonlar yaratır. bu simülatörün bir şeyler yapmasını sağlar.
Ed James

Genel olarak, herhangi bir programın büyük çoğunluğu için (bütünüyle olmasa da) “tek başına hiçbir şey yapamaz” diyemez misiniz? "Ana" kodun bir cpp'ye gittiğini mi söylüyorsunuz, ama başka hiçbir şey yapmıyor mu?
TED

Bu durumda biraz farklı. Yazdığımız kod temelde bir kitaplıktır ve kullanıcı simülasyonlarını bunun üzerine, aslında çalıştırılabilir olarak oluşturur. Bunu openGL gibi düşünün -> bir sürü işlev ve nesne elde edersiniz, ancak bunları çalıştırabilecek bir cpp dosyası olmadan işe yaramazlar.
Ed James

0

Şablon kodu yalnızca başlıklarda olmalıdır. Bunun dışında satır içi satırlar dışındaki tüm tanımlamalar .cpp içinde olmalıdır. Bunun en iyi argümanı, aynı kurala uyan std kütüphanesi uygulamaları olacaktır. Std lib geliştiricilerinin bu konuda doğru olacağını kabul etmiyorsunuz.


Hangi stdlibs? GCC en libstdc++görünüyor (AFAICS) hemen hemen hiçbir şey koymak src& neredeyse herşeyi includeo zorunluluk 'bir başlıkta olsun ya da olmasın,. Bu yüzden bunun doğru / yararlı bir alıntı olduğunu düşünmüyorum. Her neyse, stdlibs'in kullanıcı kodu için bir model olduğunu düşünmüyorum: Açıkça yüksek vasıflı kodlayıcılar tarafından yazılıyorlar, ancak kullanılacak , okunmuyorlar: çoğu kodlayıcının düşünmesi gerekmeyen yüksek karmaşıklığı soyutlıyorlar , _Reserved __nameskullanıcı ile çatışmaları önlemek için her yerde çirkin gerekir , yorum ve boşluk tavsiye ediyorum aşağıda, vb. Bunlar dar bir şekilde örnek.
underscore_d

0

İş arkadaşınızın başlıkta yürütülebilir kod yazma sürecine girmediği sürece haklı olduğunu düşünüyorum. Doğru denge, bence, .ads dosyasının kullanıcıları ve çocukları için paketin mükemmel bir arayüz tanımını verdiği GNAT Ada tarafından belirtilen yolu takip etmektir.

Ted bu arada, Ada'da birkaç yıl önce yazdığınız ve artık mevcut olmayan CLIPS kütüphanesine bağlanan son soruya bu forumda bir göz attınız mı (ilgili Web sayfaları şimdi kapatıldı). Eski bir Clips sürümüne yapılmış olsa bile, bu bağlama bir Ada 2012 programı içinde CLIPS çıkarsama motorunu kullanmak isteyen biri için iyi bir başlangıç ​​örneği olabilir.


1
Lol. 2 yıl sonra, birisini ele geçirmenin garip bir yolu. Hala bir kopyam olup olmadığını kontrol edeceğim, ancak büyük olasılıkla yok. Adada kodumu yapabilmek için bunu bir AI sınıfı için yaptım, ama bilerek birinin utanmadan onu alması ve onunla bir şeyler yapması umuduyla proje CC0'ı (aslında uncoprighted) yaptı.
TED
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.