面试题答案
一键面试同一编译单元内的初始化
在同一编译单元内,为避免未定义行为,应按照依赖关系顺序初始化全局对象。即先初始化 C
,再初始化 B
,最后初始化 A
。C++ 保证全局对象按照其定义顺序初始化,因此定义顺序要符合依赖关系。
示例代码如下:
#include <iostream>
class C {
public:
C() { std::cout << "Initializing C" << std::endl; }
};
class B {
public:
B(C& c) : c(c) { std::cout << "Initializing B" << std::endl; }
private:
C& c;
};
class A {
public:
A(B& b) : b(b) { std::cout << "Initializing A" << std::endl; }
private:
B& b;
};
C c;
B b(c);
A a(b);
int main() {
std::cout << "Main function" << std::endl;
return 0;
}
不同动态链接库中的初始化
-
使用跨库依赖管理:如果这些对象在不同动态链接库中,需要确保链接库的加载顺序符合依赖关系。在 Linux 系统中,可以通过
LD_PRELOAD
环境变量或链接器选项-Wl,--dependency-order
来控制链接库加载顺序。在 Windows 系统中,可以通过修改项目属性中的链接器选项或在代码中使用LoadLibrary
函数按依赖顺序加载动态链接库。 -
使用单例模式:可以将全局对象封装为单例模式,单例模式保证对象在第一次使用时初始化。这种方式在不同动态链接库中使用时,能有效避免因初始化顺序问题导致的未定义行为。
示例代码如下(以 Linux 为例,展示单例模式在不同动态链接库中的使用):
C 库(libC.so)
// C.h
#ifndef C_H
#define C_H
class C {
public:
static C& getInstance();
private:
C() { std::cout << "Initializing C in libC.so" << std::endl; }
~C() = default;
C(const C&) = delete;
C& operator=(const C&) = delete;
};
#endif
// C.cpp
#include "C.h"
#include <iostream>
C& C::getInstance() {
static C instance;
return instance;
}
B 库(libB.so)
// B.h
#ifndef B_H
#define B_H
#include "C.h"
class B {
public:
static B& getInstance();
private:
B(C& c) : c(c) { std::cout << "Initializing B in libB.so" << std::endl; }
~B() = default;
B(const B&) = delete;
B& operator=(const B&) = delete;
C& c;
};
#endif
// B.cpp
#include "B.h"
#include <iostream>
B& B::getInstance() {
static B instance(C::getInstance());
return instance;
}
A 库(libA.so)
// A.h
#ifndef A_H
#define A_H
#include "B.h"
class A {
public:
static A& getInstance();
private:
A(B& b) : b(b) { std::cout << "Initializing A in libA.so" << std::endl; }
~A() = default;
A(const A&) = delete;
A& operator=(const A&) = delete;
B& b;
};
#endif
// A.cpp
#include "A.h"
#include <iostream>
A& A::getInstance() {
static A instance(B::getInstance());
return instance;
}
主程序
#include <iostream>
#include "A.h"
int main() {
A& a = A::getInstance();
std::cout << "Main function" << std::endl;
return 0;
}
通过上述方法,无论是在同一编译单元内还是在不同动态链接库中,都能正确初始化全局对象,避免未定义行为。