自己怎么创建免费网站,php网站开发流程步骤,陕西网站制作qq群,wordpress怎么设计这个问题是刚了解预编译的时候产生的疑惑。
声明是指向编译器告知某个变量、函数或类的存在及其类型#xff0c;但并不分配实际的存储空间。声明的主要目的是让编译器知道如何解析程序中的符号引用。定义不仅告诉编译器实体的存在#xff0c;还会为该实体分配存储空间#…这个问题是刚了解预编译的时候产生的疑惑。
声明是指向编译器告知某个变量、函数或类的存在及其类型但并不分配实际的存储空间。声明的主要目的是让编译器知道如何解析程序中的符号引用。定义不仅告诉编译器实体的存在还会为该实体分配存储空间对于变量或者提供具体的实现对于函数。定义只能出现一次以避免重复定义错误。类定义描述了一个类型或称“蓝图”它定义了该类型的成员变量、成员函数以及其他特性。类定义本身并不分配内存它只是提供了创建对象的模板。【很明显与函数定义和变量定义很不相同】对象定义则是基于某个类创建的具体实例它会在内存中分配空间。编译器将类中声明的函数视为内联函数。当你在类定义内部定义成员函数时即直接在类体内部提供函数体这些函数默认为是inline的如果使用 inline 则意味着编译器会在调用此函数的地方把函数的目标代码直接插入而不是放置一个真正的函数调用实际作用就是这个函数事实上已经不再存在而是像宏一样被就地展开了因此不存在重复定义的问题。
总而言之类定义其实只是描述了一个类型并不会分配具体的空间在类内实现的函数编译器认为是inline调用时会展开成具体的代码所以不存在重复定义。因此类定义可以放到头文件中被不同的源文件引用也不会产生重复定义的问题。
两次导入头文件
预编译指令是以井号#开头的指令它们在编译器进行编译之前执行。预编译指令不是C语句因此它们不以分号;结尾。预编译指令包括但不限于以下几种
#include用于包含头文件将头文件的内容插入到源文件中。系统提供的头文件使用尖括号括起来而用户自定义的头文件使用双引号括起来。#define用于定义宏宏可以是简单的符号常量或带参数的宏。宏定义在预处理阶段会被替换成相应的文本。#if、#ifdef、#ifndef、#else、#elif、#endif这些条件编译指令用于根据条件判断是否编译某部分代码。
// add.h
int add(int, int);
class Person{
public:int age;
}// add.cpp
#include add.h
#include add.h
int add(int a, int b) {return a b;
}我们使用g -E add.cpp -o add.i的命令可以得到预编译后的文件
// 遇见#include add.h将add.h中的内容展开// 第一次执行#include add.h展开成如下内容
int add(int, int);
class Person{
public:int age;
}// 第二次执行#include add.h展开成如下内容
int add(int, int);
class Person{
public:int age;
}int add(int, int);
class Person{
public:int age;
}int add(int a, int b) {return a b;
}可以看到Person这个类被定义了两次很明显是不合理的我们可以通过#ifndef来解决。
// add.h
#ifndef __ADD_H // 如果没有定义过__ADD_H这个宏
#define __ADD_H // 那么就定义这个宏int add(int, int);
class Person{
public:int age;
}#endif第一次引入add.h的时候还没有定义过__ADD_H那么就定义这个宏以及头文件里的内容第二次引入add.h的时候已经定义过__ADD_H了那么就不将内容替换进去。对于cpp而言还可以使用#pragma once
#pragma once
int add(int, int);
class Person{
public:int age;
}定义和声明
在C中定义和声明是两个不同的概念它们各自有着明确的用途和含义。理解这两者的区别对于编写正确且高效的C代码至关重要。
声明是指向编译器告知某个变量、函数或类的存在及其类型但并不分配实际的存储空间。声明的主要目的是让编译器知道如何解析程序中的符号引用。例如
extern int a; // 声明一个名为a的整型变量但不分配内存
int add(int x, int y); // 声明一个名为add的函数但不提供实现
class MyClass; // 前置声明仅声明了MyClass的存在声明允许你在代码的一个部分提到某个实体并在另一个部分提供其实现或定义。这对于模块化编程特别有用因为它使得你可以将接口与实现分离。
定义不仅告诉编译器实体的存在还会为该实体分配存储空间对于变量或者提供具体的实现对于函数。定义只能出现一次以避免重复定义错误。
int a 10; // 定义了一个名为a的整型变量并初始化为10int add(int x, int y) {return x y;
} // 定义并实现了add函数void MyClass::myFunction() {// 函数实现
}一个实体只能有一个定义遵循“一个定义原则”ODR但在多个源文件中可以进行声明。并且定义会导致为变量分配存储空间或为函数生成机器码而声明不会。
类为什么可以放到头文件中
类或者结构体只是描述了对数据的组织方式并不需要申请空间所以是声明 因此可以在多个文件中引用。 类定义类定义描述了一个类型或称“蓝图”它定义了该类型的成员变量、成员函数以及其他特性。类定义本身并不分配内存它只是提供了创建对象的模板。对象定义对象定义则是基于某个类创建的具体实例它会在内存中分配空间。 通常我们会将类定义在头文件中在.cpp文件中实现方法cpp中的类函数才会生成字节码才是真实的类方法实现定义而.h中的类函数则相当于只是一个【接口声明】不会生成真正的代码。 编译器将类中声明的函数视为内联函数。因此当您调用这个类函数时有两个选项 默认视为内联当你在类定义内部定义成员函数时即直接在类体内部提供函数体这些函数默认为是inline的如果使用 inline 则意味着编译器会在调用此函数的地方把函数的目标代码直接插入而不是放置一个真正的函数调用实际作用就是这个函数事实上已经不再存在而是像宏一样被就地展开了因此不存在重复定义的问题。弱符号在某些情况下如果编译器决定不对某个inline函数进行内联它会将该函数作为“弱符号”处理。这意味着尽管该函数在多个翻译单元中有定义但在链接阶段只会保留一个副本。链接器会选择其中一个定义作为最终使用的版本而忽略其他重复的定义。这确保了即使有多个定义存在也不会导致链接错误。 假设我们有一个简单的类定义如下
// MyClass.h
#pragma onceclass MyClass {
public:void inlineFunction() { std::cout Inline function called std::endl; } // 内联函数void nonInlineFunction(); // 声明非内联函数
};然后在对应的.cpp文件中定义非内联成员函数
// MyClass.cpp
#include MyClass.h
#include iostreamvoid MyClass::nonInlineFunction() { // 定义非内联函数std::cout Non-inline function called std::endl;
}在另一个源文件中使用这个类
// main.cpp
#include MyClass.hint main() {MyClass obj;obj.inlineFunction();obj.nonInlineFunction();return 0;
}在这个例子中
inlineFunction 是在类定义内部定义的因此它被视为inline函数。即使在多个源文件中包含MyClass.h也不会违反ODR因为这些定义是相同的。nonInlineFunction 的定义只存在于MyClass.cpp中符合ODR的要求因为它在整个程序中只有一个定义。