如何组织编写模板程序

c++ 模版函数的定义和实现放在一个文件中 今天使用模版函数,遇到一个问题。 当我定义一个类,并且将它的一个成员函数定义为模版类型时,需要将该模版函数的实现与该类放在一个文件中。 比如在a.h中定义了class a: class a { ... template a(const T& t); }; 其中函数使用了模版,那么该模版函数的实现需要也放在a.h中,如果放在a.cpp中,那么在使用时可能会出现 error LNK 阅读详情

如何组织编写模板程序 



前言
常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL, 以及Boost的模板类,都能体会到这样的滋味:接口简单,操作复杂。
我在5年前开始使用模板,那时我看到了MFC的容器类。直到去年我还没有必要自己编写模板类。可是在我需要自己编写模板类时,我首先遇到的事实却是“传统”编程方法(在*.h文件声明,在*.cpp文件中定义)不能用于模板。于是我花费一些时间来了解问题所在及其解决方法。
本文对象是那些熟悉模板但还没有很多编写模板经验的程序员。本文只涉及模板类,未涉及模板函数。但论述的原则对于二者是一样的。
问题的产生
通过下例来说明问题。例如在array.h文件中有模板类array:
// array.h
template <typename T, int SIZE>
class array
{
    T data_[SIZE];
    array (const array& other);
    const array& operator = (const array& other);
public:
    array(){};
    T& operator[](int i) {return data_[i];}
    const T& get_elem (int i) const {return data_[i];}
    void set_elem(int i, const T& value) {data_[i] = value;}
    operator T*() {return data_;}      
};            
            
然后在main.cpp文件中的主函数中使用上述模板:
// main.cpp
#i nclude "array.h"
int main(void)
{
array<int, 50> intArray;
intArray.set_elem(0, 2);
int firstElem = intArray.get_elem(0);
int* begin = intArray;
}
        
这时编译和运行都是正常的。程序先创建一个含有50个整数的数组,然后设置数组的第一个元素值为2,再读取第一个元素值,最后将指针指向数组起点。
但如果用传统编程方式来编写会发生什么事呢?我们来看看:
将array.h文件分裂成为array.h和array.cpp二个文件(main.cpp保持不变)
// array.h        
template <typename T, int SIZE>
class array
{
      T data_[SIZE];
      array (const array& other);
      const array& operator = (const array& other);
  public:
      array(){};
      T& operator[](int i);
      const T& get_elem (int i) const;
      void set_elem(int i, const T& value);
      operator T*();      
};        
    
// array.cpp
#i nclude "array.h"
template<typename T, int SIZE> T& array<T, SIZE>::operator [](int i)
    {
    return data_[i];
    }
template<typename T, int SIZE> const T& array<T, SIZE>::get_elem(int i) const
    {
    return data_[i];
    }
template<typename T, int SIZE> void array<T, SIZE>::set_elem(int i, const T& value)
    {
    data_[i] = value;
    }
template<typename T, int SIZE> array<T, SIZE>::operator T*()
    {
    return data_;
    }
        
编译时会出现3个错误。问题出来了:
  为什么错误都出现在第一个地方?
  为什么只有3个链接出错?array.cpp中有4个成员函数。
  
要回答上面的问题,就要深入了解模板的实例化过程。
模板实例化
程序员在使用模板类时最常犯的错误是将模板类视为某种数据类型。所谓类型参量化(parameterized types)这样的术语导致了这种误解。模板当然不是数据类型,模板就是模板,恰如其名:
  编译器使用模板,通过更换模板参数来创建数据类型。这个过程就是模板实例化(Instantiation)。
  从模板类创建得到的类型称之为特例(specialization)。 
  模板实例化取决于编译器能够找到可用代码来创建特例(称之为实例化要素,
  point of instantiation)。
  要创建特例,编译器不但要看到模板的声明,还要看到模板的定义。
  模板实例化过程是迟钝的,即只能用函数的定义来实现实例化。


再回头看上面的例子,可以知道array是一个模板,array<int, 50>是一个模板实例 - 一个类型。从array创建array<int, 50>的过程就是实例化过程。实例化要素体现在main.cpp文件中。如果按照传统方式,编译器在array.h文件中看到了模板的声明,但没有模板的定义,这样编译器就不能创建类型array<int, 50>。但这时并不出错,因为编译器认为模板定义在其它文件中,就把问题留给链接程序处理。
现在,编译array.cpp时会发生什么问题呢?编译器可以解析模板定义并检查语法,但不能生成成员函数的代码。它无法生成代码,因为要生成代码,需要知道模板参数,即需要一个类型,而不是模板本身。
这样,链接程序在main.cpp 或 array.cpp中都找不到array<int, 50>的定义,于是报出无定义成员的错误。
至此,我们回答了第一个问题。但还有第二个问题,在array.cpp中有4个成员函数,链接器为什么只报了3个错误?回答是:实例化的惰性导致这种现象。在main.cpp中还没有用上operator[],编译器还没有实例化它的定义。
解决方法
认识了问题,就能够解决问题:
  在实例化要素中让编译器看到模板定义。
  用另外的文件来显式地实例化类型,这样链接器就能看到该类型。
  使用export关键字。
前二种方法通常称为包含模式,第三种方法则称为分离模式。
第一种方法意味着在使用模板的转换文件中不但要包含模板声明文件,还要包含模板定义文件。在上例中,就是第一个示例,在array.h中用行内函数定义了所有的成员函数。或者在main.cpp文件中也包含进array.cpp文件。这样编译器就能看到模板的声明和定义,并由此生成array<int, 50>实例。这样做的缺点是编译文件会变得很大,显然要降低编译和链接速度。
第二种方法,通过显式的模板实例化得到类型。最好将所有的显式实例化过程安放在另外的文件中。在本例中,可以创建一个新文件templateinstantiations.cpp:
// templateinstantiations.cpp                
#i nclude "array.cpp"
template class array <int, 50>; // 显式实例化
        
array<int, 50>类型不是在main.cpp中产生,而是在templateinstantiations.cpp中产生。这样链接器就能够找到它的定义。用这种方法,不会产生巨大的头文件,加快编译速度。而且头文件本身也显得更加“干净”和更具有可读性。但这个方法不能得到惰性实例化的好处,即它将显式地生成所有的成员函数。另外还要维护templateinstantiations.cpp文件。
第三种方法是在模板定义中使用export关键字,剩下的事就让编译器去自行处理了。当我在
Stroustrup的书中读到export时,感到非常兴奋。但很快就发现VC 6.0不支持它,后来又发现根本没有编译器能够支持这个关键字(第一个支持它的编译器要在2002年底才问世)。自那以后,我阅读了不少关于export的文章,了解到它几乎不能解决用包含模式能够解决的问题。欲知更多的export关键字,建议读读Herb Sutter撰写的文章。
结论
要开发模板库,就要知道模板类不是所谓的"原始类型",要用其它的编程思路。本文目的不是要吓唬那些想进行模板编程的程序员。恰恰相反,是要提醒他们避免犯下开始模板编程时都会出现的错误。
 


//////////////////////////////
http://www.cnblogs.com/xgchang/archive/2004/11/12/63139.aspx
甚至是在定义非内联函数时,模板的头文件中也会放置所有的声明和定义。这似乎违背了通常的头文件规则:“不要在分配存储空间前放置任何东西”,这条规则是为了防止在连接时的多重定义错误。但模板定义很特殊。由template<...>处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直出于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉模板的多重定义,所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。
为什么C++编译器不能支持对模板的分离式编译 
刘未鹏(pongba) /文
首先,C++标准中提到,一个编译单元[translation unit]是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件,后者拥有PE[Portable Executable,即windows可执行文件]文件格式,并且本身包含的就已经是二进制码,但是,不一定能够执行,因为并不保证其中一定有main函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(linker)进行连接成为一个.exe文件。 
举个例子: 
//---------------test.h-------------------// 
void f();//这里声明一个函数f 
//---------------test.cpp--------------// 
#i nclude”test.h” 
void f() 
{ 
…//do something 
} //这里实现出test.h中声明的f函数 
//---------------main.cpp--------------// 
#i nclude”test.h” 
int main() 
{ 
f(); //调用f,f具有外部连接类型 
} 
在这个例子中,test. cpp和main.cpp各被编译成为不同的.obj文件[姑且命名为test.obj和main.obj],在main.cpp中,调用了f函数,然而当编译器编译main.cpp时,它所仅仅知道的只是main.cpp中所包含的test.h文件中的一个关于void f();的声明,所以,编译器将这里的f看作外部连接类型,即认为它的函数实现代码在另一个.obj文件中,本例也就是test.obj,也就是说,main.obj中实际没有关于f函数的哪怕一行二进制代码,而这些代码实际存在于test.cpp所编译成的test.obj中。在main.obj中对f的调用只会生成一行call指令,像这样: 
call f [C++中这个名字当然是经过mangling[处理]过的] 
在编译时,这个call指令显然是错误的,因为main.obj中并无一行f的实现代码。那怎么办呢?这就是连接器的任务,连接器负责在其它的.obj中[本例为test.obj]寻找f的实现代码,找到以后将call f这个指令的调用地址换成实际的f的函数进入点地址。需要注意的是:连接器实际上将工程里的.obj“连接”成了一个.exe文件,而它最关键的任务就是上面说的,寻找一个外部连接符号在另一个.obj中的地址,然后替换原来的“虚假”地址。 
这个过程如果说的更深入就是: 
call f这行指令其实并不是这样的,它实际上是所谓的stub,也就是一个 
jmp 0x23423[这个地址可能是任意的,然而关键是这个地址上有一行指令来进行真正的call f动作。也就是说,这个.obj文件里面所有对f的调用都jmp向同一个地址,在后者那儿才真正”call”f。这样做的好处就是连接器修改地址时只要对后者的call XXX地址作改动就行了。但是,连接器是如何找到f的实际地址的呢[在本例中这处于test.obj中],因为.obj于.exe的格式都是一样的,在这样的文件中有一个符号导入表和符号导出表[import table和export table]其中将所有符号和它们的地址关联起来。这样连接器只要在test.obj的符号导出表中寻找符号f[当然C++对f作了mangling]的地址就行了,然后作一些偏移量处理后[因为是将两个.obj文件合并,当然地址会有一定的偏移,这个连接器清楚]写入main.obj中的符号导入表中f所占有的那一项。 
这就是大概的过程。其中关键就是: 
编译main.cpp时,编译器不知道f的实现,所有当碰到对它的调用时只是给出一个指示,指示连接器应该为它寻找f的实现体。这也就是说main.obj中没有关于f的任何一行二进制代码。 
编译test.cpp时,编译器找到了f的实现。于是乎f的实现[二进制代码]出现在test.obj里。 
连接时,连接器在test.obj中找到f的实现代码[二进制]的地址[通过符号导出表]。然后将main.obj中悬而未决的call XXX地址改成f实际的地址。 
完成。
然而,对于模板,你知道,模板函数的代码其实并不能直接编译成二进制代码,其中要有一个“具现化”的过程。举个例子: 
//----------main.cpp------// 
template<class T> 
void f(T t) 
{} 
int main() 
{ 
…//do something 
f(10); //call f<int> 编译器在这里决定给f一个f<int>的具现体 
…//do other thing 
} 
也就是说,如果你在main.cpp文件中没有调用过f,f也就得不到具现,从而main.obj中也就没有关于f的任意一行二进制代码!!如果你这样调用了: 
f(10); //f<int>得以具现化出来 
f(10.0); //f<double>得以具现化出来 
这样main.obj中也就有了f<int>,f<double>两个函数的二进制代码段。以此类推。 
然而具现化要求编译器知道模板的定义,不是吗? 
看下面的例子:[将模板和它的实现分离] 
//-------------test.h----------------// 
template<class T> 
class A 
{ 
public: 
void f(); //这里只是个声明 
}; 
//---------------test.cpp-------------// 
#i nclude”test.h” 
template<class T> 
void A<T>::f() //模板的实现,但注意:不是具现 
{ 
…//do something 
} 
//---------------main.cpp---------------// 
#i nclude”test.h” 
int main() 
{ 
A<int> a; 
a. f(); //编译器在这里并不知道A<int>::f的定义,因为它不在test.h里面 
//于是编译器只好寄希望于连接器,希望它能够在其他.obj里面找到 
//A<int>::f的实现体,在本例中就是test.obj,然而,后者中真有A<int>::f的 
//二进制代码吗?NO!!!因为C++标准明确表示,当一个模板不被用到的时 
//侯它就不该被具现出来,test.cpp中用到了A<int>::f了吗?没有!!所以实 
//际上test.cpp编译出来的test.obj文件中关于A::f的一行二进制代码也没有 
//于是连接器就傻眼了,只好给出一个连接错误 
//但是,如果在test.cpp中写一个函数,其中调用A<int>::f,则编译器会将其//具现出来,因为在这个点上[test.cpp中],编译器知道模板的定义,所以能//够具现化,于是,test.obj的符号导出表中就有了A<int>::f这个符号的地 
//址,于是连接器就能够完成任务。 
}
关键是:在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找[当遇到未决符号时它会寄希望于连接器]。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会具现化出来,所以,当编译器只看到模板的声明时,它不能具现化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的具现体时,编译器懒得去具现,所以,整个工程的.obj中就找不到一行模板具现体的二进制代码,于是连接器也黔
 
 
/////////////////////////////////
http://dev.youkuaiyun.com/develop/article/19/19587.shtm
 C++模板代码的组织方式 ——包含模式(Inclusion Model)     选择自 sam1111 的 Blog  
关键字   Template Inclusion Model 
出处   C++ Template: The Complete Guide


说明:本文译自《C++ Template: The Complete Guide》一书的第6章中的部分内容。最近看到C++论坛上常有关于模板的包含模式的帖子,联想到自己初学模板时,也为类似的问题困惑过,因此翻译此文,希望对初学者有所帮助。
模板代码有几种不同的组织方式,本文介绍其中最流行的一种方式:包含模式。
链接错误
大多数C/C++程序员向下面这样组织他们的非模板代码:
         ·类和其他类型全部放在头文件中,这些头文件具有.hpp(或者.H, .h, .hh, .hxx)扩展名。
         ·对于全局变量和(非内联)函数,只有声明放在头文件中,而定义放在点C文件中,这些文件具有.cpp(或者.C, .c, .cc, .cxx)扩展名。
 
这种组织方式工作的很好:它使得在编程时可以方便地访问所需的类型定义,并且避免了来自链接器的“变量或函数重复定义”的错误。
 
由于以上组织方式约定的影响,模板编程新手往往会犯一个同样的错误。下面这一小段程序反映了这种错误。就像对待“普通代码”那样,我们在头文件中定义模板:
 
// basics/myfirst.hpp 
#ifndef MYFIRST_HPP 
#define MYFIRST_HPP 
// declaration of template
template <typename T>
void print_typeof (T const&);
#endif // MYFIRST_HPP
 
print_typeof()声明了一个简单的辅助函数用来打印一些类型信息。函数的定义放在点C文件中:
// basics/myfirst.cpp
#i nclude <iostream>
#i nclude <typeinfo>
#i nclude "myfirst.hpp" 
 
// implementation/definition of template
template <typename T> 
void print_typeof (T const& x) 
{
    std::cout << typeid(x).name() << std::endl;
}
 
这个例子使用typeid操作符来打印一个字符串,这个字符串描述了传入的参数的类型信息。
最后,我们在另外一个点C文件中使用我们的模板,在这个文件中模板声明被#i nclude:
// basics/myfirstmain.cpp 
#i nclude "myfirst.hpp" 
// use of the template
int main() 
{
    double ice = 3.0; 
    print_typeof(ice);  // call function template for type double
}


大部分C++编译器(Compiler)很可能会接受这个程序,没有任何问题,但是链接器(Linker)大概会报告一个错误,指出缺少函数print_typeof()的定义。
这个错误的原因在于,模板函数print_typeof()的定义还没有被具现化(instantiate)。为了具现化一个模板,编译器必须知道哪一个定义应该被具现化,以及使用什么样的模板参数来具现化。不幸的是,在前面的例子中,这两组信息存在于分开编译的不同文件中。因此,当我们的编译器看到对print_typeof()的调用,但是没有看到此函数为double类型具现化的定义时,它只是假设这样的定义在别处提供,并且创建一个那个定义的引用(链接器使用此引用解析)。另一方面,当编译器处理myfirst.cpp时,该文件并没有任何指示表明它必须为它所包含的特殊参数具现化模板定义。
头文件中的模板
解决上面这个问题的通用解法是,采用与我们使用宏或者内联函数相同的方法:我们将模板的定义包含进声明模板的头文件中。对于我们的例子,我们可以通过将#i nclude "myfirst.cpp"添加到myfirst.hpp文件尾部,或者在每一个使用我们的模板的点C文件中包含myfirst.cpp文件,来达到目的。当然,还有第三种方法,就是删掉myfirst.cpp文件,并重写myfirst.hpp文件,使它包含所有的模板声明与定义:


// basics/myfirst2.hpp
#ifndef MYFIRST_HPP 
#define MYFIRST_HPP 
#i nclude <iostream> 
#i nclude <typeinfo> 
 
// declaration of template 
template <typename T> 
void print_typeof (T const&); 
// implementation/definition of template 
template <typename T> 
void print_typeof (T const& x) 
{
    std::cout << typeid(x).name() << std::endl;
}
#endif // MYFIRST_HPP
 
这种组织模板代码的方式就称作包含模式。经过这样的调整,你会发现我们的程序已经能够正确编译、链接、执行了。
从这个方法中我们可以得到一些观察结果。最值得注意的一点是,这个方法在相当程度上增加了包含myfirst.hpp的开销。在这个例子中,这种开销并不是由模板定义自身的尺寸引起的,而是由这样一个事实引起的,即我们必须包含我们的模板用到的头文件,在这个例子中是<iostream>和<typeinfo>。你会发现这最终导致了成千上万行的代码,因为诸如<iostream>这样的头文件也包含了和我们类似的模板定义。
这在实践中确实是一个问题,因为它增加了编译器在编译一个实际程序时所需的时间。我们因此会在以后的章节中验证其他一些可能的方法来解决这个问题。但无论如何,现实世界中的程序花一小时来编译链接已经是快的了(我们曾经遇到过花费数天时间来从源码编译的程序)。
抛开编译时间不谈,我们强烈建议如果可能尽量按照包含模式组织模板代码。
另一个观察结果是,非内联模板函数与内联函数和宏的最重要的不同在于:它并不会在调用端展开。相反,当模板函数被具现化时,会产生此函数的一个新的拷贝。由于这是一个自动的过程,编译器也许会在不同的文件中产生两个相同的拷贝,从而引起链接器报告一个错误。理论上,我们并不关心这一点:这是编译器设计者应当关心的事情。实际上,大多数时候一切都运转正常,我们根本就不用处理这种状况。然而,对于那些需要创建自己的库的大型项目,这个问题偶尔会显现出来。
 
最后,需要指出的是,在我们的例子中,应用于普通模板函数的方法同样适用于模板类的成员函数和静态数据成员,以及模板成员函数。
C++编译流程 C/C++是编译型高级语言,程序要执行,必须要有编译器和链接器。编译过程分为四步:预处理、编译、汇编、链接。 阅读详情

相关推荐

C++模板元编程详细教程(之二)

C++模板元编程详细教程系列文章的第二篇,主要介绍了模板的参数自动推导。

fl2011sx的博客 5087

模板类声明和定义为何要写一起

http://www.gois.ws/showfile.asp?id=460:0:0如何组织编写模板程序 发表日期: 1/21/2003 12:28:58 PM 发表人: Nemanja Trifunovic 前言常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL, 以及Boost的

~。~||| 1万+

生态学家都在用的MixSIAR:用R语言搞定稳定同位素混合模型(从数据整理到结果解读)

本文详细介绍了生态学家常用的MixSIAR工具,通过R语言实现稳定同位素混合模型分析,从数据整理到结果解读全流程。文章涵盖环境准备、模型构建、参数设置、运行诊断及可视化等关键步骤,帮助研究者科学分析河口鱼类食物来源,适用于生态学研究和湿地保护政策制定。

weixin_28683065的博客 439

为什么模板的声明和实现不能分写在.h与.cpp中

《C++编程思想》第15章(第300页)说明了原因: 模板定义很特殊。由template<…>处理的任何东西都意味着编译器在当时不为它分配存储空间(没有具体的函数时不会对模板实例化),它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。 定义一个类一般都是在头文件中进行类声明,在cpp文件中实现,但使用模板时应注意目前的C++编译器还无法分离编译,最好将实现代码和声明...

寂蝶难双,窃窃思羽. 4741

VCSA 6.7证书过期别慌!手把手教你通过SSH修改系统时间恢复登录(附STS证书修复脚本)

本文详细介绍了VCSA 6.7证书过期的应急处理方案,包括通过SSH修改系统时间临时恢复登录、全面更新证书体系以及专项修复STS证书的实战步骤。文章还提供了后期验证和系统加固的建议,帮助运维人员有效应对证书过期问题,确保虚拟化平台的稳定运行。

weixin_31292729的博客 416

先进封装类别分析与典型案例

当制程微缩逼近物理极限、单颗大芯片(reticle limit)面积受限于光罩尺寸,且逻辑、存储、射频、传感等功能无法用同一套工艺最经济地制造时,性能增益的来源从 "晶体管密度" 转向 "互连密度"。先进封装的本质,是在芯片外部用更短、更密、更宽的路径把多个裸 die(die)互连起来,换取更高带宽、更低延迟、更小体积和异构集成能力。行业通常按划分为 2D、2.5D、3D、扇出、SiP 五大谱系,Chiplet/UCIe 则是贯穿其上的设计方法论,玻璃基板、共封装光学(CPO)是正在成型的新分支。

0 34

【转】如何组织编写模板程序

skycrapper的专栏 549

C-08-实战:搭建一个AI客服机器人(接入微信企微)

客服是一人公司最大的时间杀手。本文教你搭建一个AI客服机器人,能自动回答80%的常见问题,只把复杂问题转给人工。含微信公众号和企业微信接入完整代码。

Johnny2004的专栏 88

如何组织编写模板程序 (How To Organize Template Source Code)

如何组织编写模板程序 前言常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL, 以及Boost的模板类,都能体会到这样的滋味:接口简单,操作复杂。我在5年前开始使用模板,那时我看到了MFC的容器类。直到去年我还没有必要自己编写模板类。可是在我需要自己编写模板类时,我首先遇到的事实却是“传统”...

weixin_30835933的博客 114

AI应用架构师进阶:智能控制系统架构的多Agent设计

本文将从“AI应用架构师”的视角,系统讲解智能控制系统中多Agent架构的设计方法。理解多Agent系统的核心特性与在智能控制场景中的优势;掌握多Agent架构的设计流程:从需求分析到角色划分、通信设计、决策实现;通过实战案例(智能家居控制场景),手把手带你落地一个多Agent智能控制系统;探讨进阶话题:动态Agent组织、安全性设计、与大语言模型(LLM)的融合等。在多Agent系统中,Agent(智能体)是具备自主决策能力的最小执行单元。

小程序开发 1086

扩散模型实战:从零开始训练手写数字生成模型

手写数字的数据集是绝大部分炼丹师的深度学习初恋,这篇博客以代码为主,手把手带读者搭建一个基于扩散模型的手写数字生成模型,非常适合刚接触扩散模型的初学者学习。

沉迷单车的追风少年 1914

翻译:《实用的Python编程》03_00_Overview

目录 | 上一节 (2 处理数据) | 下一节 (4 类和对象) 3. 程序组织 到目前为止,我们已经学习了一些 Python 基础知识并编写了一些简短的脚本。但是,当开始编写更大的程序时,我们会想要有条理地组织这些程序。本节将深入讨论关于函数编写,错误处理以及模块的更多细节。最后,您应该能够编写跨多个文件的,并细分为函数的程序。我们还将提供一些有用的代码模板,以编写更有用的脚本。 3.1 函数和脚本编写 3.2 关于函数的更多细节 3.3 异常处理 3.4 模块 3.5 主模块 3.6 关于拥抱灵活性的

codists的博客 233

推荐一个直接用于项目开发的PID库!很好用,很稳定

关注、星标公众号,不错过精彩内容大家好,我是小麦。最近有朋友问到有没有现成的C语言PID库。当然有了!现在我就准备给大家安利一下了。一般同学会去某度上搜,看到各种各样版本的PID示例,或者...

一名热爱技术的工程师的博客。分享技术干货,记录美好生活。永远相信美好的事情即将发生。 9025

如何使用Junit编写和组织测试程序

<!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--><script type="text/javascript"

zgqtxwd的专栏 412

MathType公式使用技巧汇总——Mathtype怎么在word中编辑公式?论文中公式有哪技巧?有哪些注意事项?论文中的公式怎么写?

如果word中到公式编号(2-1)中间的符号如下图........ .......类似与省略号的,这是手动打的空格,公式和编号的位置都是手动敲空格操作的,这种就只是使用MathType写了公式而已,格式上还是很不方便的。**如果要修改公示,直接双击文档中的公式,就会自动弹出MathType公式编辑窗口,进行修改。如果要修改公示,直接双击文档中的公式,就会自动弹出MathType公式编辑窗口,进行修改。而第二种,是公式里面带的括号符号,他是可以随着公式结构自适应变化的。这时需要将公式插入到一行文字的某处,

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 5万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++ 类模板小结(双向链表的类模板实现)

一、类模板定义 定义一个类模板: template class 类名{ // 类定义...... };其中,template 是声明类模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个,可以是类型参数,也可以是非类型参数。类型参数由关键字class或typename及其后面的标识符构成。非类型参数由一个普通参数构成,代表模板定义中的一个常量。例: template //ty

HelloKandy's Blog 3793

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

微信小程序 template模板

如果你碰到页面布局类似的话, 可以考虑使用小程序框架 — template模板 首先创建一个模板文件夹及相应文件 在模板文件下的wxml文件中写出自己所需要的模板布局, wxml文件中可以是多个模板, 看图说话(以下为个人案例) 以下是模板块中个人所需的代码 template 使用 name 属性,作为此模板的名字。然后在内定义代码片段,除此之外跟普通写页面没有任何区别

Say_one的博客 483

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C++模板类之理解编译器的编译模板过程

C++模板类之理解编译器的编译模板过程          以前经常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL, 以及Boost的模板类,都能体会到这样的滋味:接口简单,操作复杂。     我在5年前开始使用模板,那时我看到了MFC的容器类。直到去年我还没有必要自己编写模板类。

君临天下的博客 4134

<转> 模板类 声明和定义 编译 c++

如何组织编写模板程序  前言 常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL, 以及Boost的模板类,都能体会到这样的滋味:接口简单,操作复杂。 我在5年前

杂乱的专栏 933

实现用vue方式编写小程序

项目源码地址 链接地址(github.com/cxp1539/vue…) 关于 主要作用是实现以类似Vue.js文件的方式编写小程序(将模板、JS、样式写在一个文件中),这种组织方式比较方便,而且一目了然 该项目只是简单生成了微信小程序quickstart的案例,另外增加了配置定义,和工具方法。供大家参考学习。模版,组件等更多功能可以在该项目webpack入口文件进行修改 自定义增加的功能 模版使...

weixin_34233421的博客 268
上一篇: 这样学习计算机不迷茫
下一篇: 为何现在的大学生要如此急功近利?
i丹明扬
博客等级 码龄15年 331粉丝 115原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值