Effective<1>——让自己习惯c++

<1>让自己习惯C++


条款01:视C++为一个语言联邦

今天的c++已经是个多重范型编程语言,一个同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。主要可以分为四个次语言:
1.C。C++仍是以C为基础。区块(blocks)、语句(statements)、预处理器(perprocessor)、内置数据类型(built-in data types)、数组(arrays)、指针(pointers)等。
2.Object-Oriented C++。C with Classes所诉求的:classes(包括构造函数和析构函数)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)等。
3.Template C++。泛型编程部分。template相关考虑和设计已经弥漫整个C++。
4.STL。STL是个template程序库。它对容器(containers)、迭代器(iterators)、算法(algorithms)以及函数对象(functionobjects)的规约有极佳的紧密配合与协调。
~C++高效编程守着视状况而变化,取决于你使用C++的哪一部分。

条款02:尽量以const、enum、inline替换#define

常量替换#define有两种特殊情况。
1.定义常量指针(constant pointers),由于常量定义式通常被放在头文件内(以便不同源码含入),因此有必要将指针(非指针指向之物)声明为const。若要定义一个常量的char*-based字符串,则有:
const char* const authorName="Scott Meyers";
或 const std::string authorName("Scott Meyers");
2.class专属常量。为了将常量的作用域(scope)限制与class内,你必须让他成为class的成员(member):
class GamePlayer{
private:
      static const int NumTurns=5;//常量声明式
      int scores[NumTurns];//使用该常量
      ...
};
然而所写的是NumTurns的生明式而非定义式。有些编译器需要另外提供定义式:
const int GamePlayer::NUmTurns;
同时旧式编译器也许不支持上述语法,不允许static成员在其声明式上获得初值。(此外in-class初值设定,也只允许对整数常量进行)则可将初值放入定义式中:
class CostEstimate{
private:
     static const double FudgeFactor;//static class 常量声明
     ...//位于头文件内
};
const double CostEstimate::FudgeFactor=1.35;//static class常量定义,位于实现文件内
当在class编译期间需要class常量值时,可以改用“the enum hack”补偿做法,其理论基础是“一个属于枚举类型(enumerated type)的数值可权充ints被使用:
class gamePlayer{
private:
      enum{NumTurns=5};
      int  scores[NumTurns];
      ...
}; 
请注意,我们无法利用#define创建一个class专属常量。
以及enum hack与#define更相似,获取一个const地址是合法的,而获取enum和#define的地址不合法。但是enum和#define不会导致非必要的内存分配。
最后就是,使用template inline函数的宏可以带来更大的效率:
template <typename T>
inline void callWithMax(const T&a,const T&b)
{
       f(a>b?a:b);
}
有了consts、enums、inlines,我们对预处理器(#define)的需求降低了,但#include仍是必须品,而#ifdef/#ifndef也继续扮演控制编译的角色。
~对于单纯常量,最好以const对象或enums替换#define
~对于形似函数的宏(macros),最好改用inline替换#define

条款03:尽可能使用const

const:允许你指定一个语义约束(一个“不该被改动”的对象),编译器会强制实施者项约束。
指针中,const出现在*左边,表示被指物是常量;出现在*右边,表示指针自身是常量。
void f1(const Widget* pw);//f1、f2是相同的
void f2(Widget const *pw);
STL迭代器,const声明表示,迭代器不得指向不同的东西,但所指的东西可以改变,如果希望他无法改变,就需要使用const_iterator:
std::vector<int> vec;
...
const std::vector<int>::iterator iter=vec.begin();
*iter=10;//没问题,改变的是iter所指之物
++iter;//错误,iter是const
std::vector<int>::const_iterator cIter=vec.begin();
*cIter=10;//错误,*cIter是const
++cIter;//没错,改变cIter
函数返回一个const值,可以防止其被客户改动:
class Rational{...};
const Tarional operator* (const Rational& lhs,const Tarional& rhs);

const函数内调用non-const函数,就改动了曾经做的承诺。所以我们必须使用const_cast将*this身上的const解放掉。
const operator[]完全做掉了non-const版本该做的一切,这种情况可以将const转除:
class Textblock{
public:
...
const char& operator[](std::size_t position)const
{
...
return text[position];
}
char& operator[](std::size_t position)//调用const op[]
{
return 
   const _cast<char&>(//将op[]返回值的const转除
      static_cast<const TextBlock&>(*this)[position]//为*this加上const,调用const op[]
      );
}
...
};
为了避免operator[]递归调用自己,我们必须明确指出调用的是const operator[],所以这里将*this从原始类型TextBlock&转型为const TextBlock&。
这里共有两次转型1.*this 添加const
2.const operator[]返回值中移除const


条款04:确定对象被使用前已先被初始化

通常使用C part of C++而且初始化可能招致运行期成本,那么久不保证发生初始化。一旦进入non-C parts of C++规则就有变化。这就好解释为什么array不保证其内容被初始化,而vector却需要保证。
最佳出来办法就是:永远在使用对象之前将他初始化。
int x=0;//初始化int
const char* text="A C-style string";//初始化指针
double d;
std::cin>>d;//以读取input stream的方式初始化
至于内置类型以外的东西,初始化责任在构造函数上,规则很简单:保证每一个构造函数都将对象的每一个成员初始化。
class PhoneNumber{...}
class ABEntry{
public:
     ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>&phone);
private:
   std::string theName;
   std::string theAddress;
   std::list<PhoneNumber> thePhones;
   int numTinesConsulted;
};
ABEntry::ABEntry(cosnt std::string& name,const std::string& address,const std::list<PhoneNumber>&phones)
{//赋值
     theName=name;
     theAddress=address;
     thePhones=phones;
     numTimesConsulted=0;
}
ABEntry::ABEntry(cosnt std::string& name,const std::string& address,const std::list<PhoneNumber>&phones)
://初始化
     theName(name);
     theAddress(address);
     thePhones(phones);
     numTimesConsulted(0);
{  }
不使用初始化而赋值,会使得成员对象在初始化发生时间更早,发生于这些default构造函数被调用之前,是的资源浪费。
ABEntry::ABEntry()//成员初值表
     :theName(),
      theAddress(),
      thePhones(),
      numTimesConsulted(0)
{}
规定总是在初值列中列出所有成员变量,以免还得记住那些成员变量可以无需初值。
当许多classes拥有多个构造函数时,每个构造函数都会有自己的成员初值列,就会导致很对重复的工作,这种情况下,可以合理的在初值列遗漏那些“赋值表现好的成员”,对他们改用赋值。

C++,有着十分固定的“成员初始化次序”(不会报错),base classes(基类)先于derived classes(派生类)。

在,问题涉及多个源码文件时,会发生使用某一non-local static对象的初始化使用了另一编译单元内的某个non-local static对象,他有可能尚未被初始化。
这时我们需要做的便是:将每个non-local static对象搬到自己的专属函数中(该对象在此函数内被声明为static)。喊句话说,non-local static 被local static替换了。
例:
class FileSystem{//来着你的程序库
public:
     ...
    std::size_t numDisks()const;//成员函数
     ...
};
extern Fileststem tfs;//预备给客户使用的对象

class Directory{//由程序库客户建立
public:
    directory(params);
    ...
};
Directory::Directory(params)
{
    ...
    std::size_t disks=tfs.numDisks();//使用tfs对象
    ...
}
Directory tempDir(params);//为临时文件做目录
除非tfs在tempDir之前初始化,否则tempDir会使用尚未出生的tfs。
改动:
class FileStstem{...}
FileSystem& tfs()//替换tfs对象
{//FileSystem class 中可能是个static.
    static FileSystem fs;//定义初始化一个local static对象
    return fs;//返回一个reference上述对象
}
class Directory{...};
Directory::Directory(params)
{
    ...
    std::size_t disks=tfs.numDisks();//使用tfs对象
    ...
}
Dierctory& tempDir()//替换tempDir对象
{
     static Directory td;
     return td;
}

~为内置型对象手工初始化,C++ 不保证他们初始化。
~构造函数最好使用成员初值列,而不要再构造函数本体内使用赋值。
~为免除"跨编译单元的初始化次序"问题,用local static对象替换non-local static。



















<think>嗯,用户想了解Maven的POM.xml文件结构和依赖管理。首先,我需要回忆一下POM的基本结构。记得POM是Project Object Model的缩写,是Maven项目的核心配置文件。 首先,我应该从POM的根元素开始讲起,比如<project>标签,里面包含了模型版本、groupId、artifactId、version这些基本信息。然后,用户可能想知道各个部分的作用,比如packaging类型,比如jar、war这些,还有name和description之类的元数据。 接下来是依赖管理部分。用户提到需要示例,所以我得举一个具体的依赖项例子,比如引入JUnit。要说明groupId、artifactId、version,可能还要提到scope,比如test作用域。另外,依赖传递和冲突解决也是关键点,比如如何用<exclusions>来排除不需要的传递依赖。 然后,构建配置部分,<build>标签里的内容也很重要,比如指定源码目录、资源过滤、插件配置。比如Maven Compiler插件设置Java版本,还有打包时使用的maven-jar-plugin。可能需要引用站内提到的引用[3],关于<build>的详细结构。 另外,用户可能对继承和聚合感兴趣。比如父POM通过<parent>来继承,还有多模块项目中的<modules>配置。Super POM的概念也很重要,可以提到引用[1]中的mvn help:effective-pom命令来查看有效POM。 还要注意依赖范围,比如provided、runtime等,以及可选依赖<optional>的使用场景。可能用户在实际项目中遇到过依赖冲突的问题,所以需要解释如何管理依赖版本,推荐使用<dependencyManagement>来统一版本。 最后,用户可能需要示例代码来具体展示这些配置,比如一个完整的pom.xml示例,包含依赖项和插件配置。同时,相关问题部分要引导用户深入,比如如何解决依赖冲突,多模块配置的最佳实践,或者与构建生命周期相关的插件配置。 需要确保所有行内数学表达式用$...$格式,但在这个问题里可能用不上。不过要注意格式正确,比如代码块用反引号,引用标识放在相关段落末尾。比如在提到Super POM时添加[^1],在构建配置提到引用[3]。</think>### Maven POM.xml 文件详解及依赖管理示例 #### 一、POM.xml 基础结构 POM(Project Object Model)是 Maven 项目的核心配置文件,采用 XML 格式定义项目信息、依赖关系和构建配置。以下是其核心元素: 1. **基础信息配置** ```xml <project> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo-project</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <!-- 可选值:jar/war/maven-plugin等[^2] --> <name>Demo Project</name> <description>示例项目</description> </project> ``` 2. **依赖管理** - **依赖声明**:通过 `<dependencies>` 定义项目所需库 ```xml <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> <!-- 作用域:compile/test/runtime等 --> </dependency> </dependencies> ``` - **依赖传递控制**:使用 `<exclusions>` 排除冲突依赖 ```xml <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.20</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> ``` #### 二、构建配置详解 `<build>` 元素控制项目构建行为,包含以下关键子元素[^3]: ```xml <build> <sourceDirectory>src/main/java</sourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> </plugins> </build> ``` #### 三、高级特性示例 1. **继承与聚合** - 父 POM 配置: ```xml <groupId>com.parent</groupId> <artifactId>parent-pom</artifactId> <version>1.0.0</version> <packaging>pom</packaging> ``` - 子模块引用: ```xml <modules> <module>submodule1</module> <module>submodule2</module> </modules> ``` 2. **Super POM 继承** 所有 Maven 项目都继承自 Super POM,可通过命令查看合并后的有效 POM: ```shell mvn help:effective-pom ``` 此命令会显示实际生效的配置组合。 #### 四、完整示例 ```xml <project> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo-app</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.2</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值