2011-01-26 38 views
12

Sau khi đọc rất nhiều câu hỏi liên quan đến khởi tạo các biến tĩnh Tôi vẫn không chắc chắn làm thế nào điều này áp dụng cho const biến ở mức không gian tên.const biến trong tập tin tiêu đề và tĩnh khởi tạo fiasco

Tôi có loại đoạn mã sau vào một tiêu đề tập tin config.h được tạo ra bởi việc xây dựng kịch bản:

static const std::string path1 = "/xyz/abc"; 
static const std::string path2 = "/etc"; 

Theo những gì tôi đã đọc các từ khóa static là không cần thiết, thậm chí bị phản đối ở đây.

Câu hỏi của tôi: Mã trên có dễ bị thất bại khởi tạo tĩnh không?

Nếu tôi có sau đây trong một tiêu đề tập tin myclass.h:

class MyClass 
{ 
public: 
    MyClass(const std::string& str) : m_str(str) {} 
    std::string Get() const { return m_str; } 

private: 
    std::string m_str; 
} 

const MyClass myclass1("test"); 

này sẽ gây ra bất kỳ vấn đề với khởi tạo tĩnh?

Nếu tôi hiểu đúng, do const biến có liên kết nội bộ nên không có vấn đề gì trong cả hai trường hợp?

Edit: (do dribeas câu trả lời)

Có lẽ tôi nên đề cập đến mà tôi đang quan tâm trong trường hợp sử dụng như:

Trong main.cpp:

#include <config.h> 
#include <myclass.h> 

std::string anotherString(path1 + myclass1.Get()); 

int main() 
{ 
    ... 
} 

Câu hỏi khác về việc sử dụng này case: Trình biên dịch có tối ưu hóa path2 trong trường hợp này không?

Trả lời

9

Tôi đã cố gắng lấy thông tin cần thiết ngay từ tài liệu chuẩn C++ 03. Dưới đây là những gì tôi tìm thấy:

Về const static tờ khai:

Theo mục 3.5.3 đối tượng quy định tại mức namespace và tuyên bố constnội bộ liên kết theo mặc định. static cũng khai báo một đối tượng mức không gian tên để có liên kết nội bộ, do đó không cần phải khai báo một đối tượng static const.

Cũng theo Phụ lục D.2

The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.5).

Về khởi thất bại tĩnh:

Kể từ khi các biến được định nghĩa trong một tập tin tiêu đề họ luôn luôn được xác định trước bất kỳ đối tượng tĩnh khác sử dụng chúng .

Từ phần 3.6.2.1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

Trả lời 1: Điều này có nghĩa qua các biến để một constuctor đối tượng tĩnh nên được tốt.

Trả lời 2: Tuy nhiên một vấn đề có thể xảy ra nếu các biến được tham chiếu từ một constructor không inline của một đối tượng tĩnh:

Cả trong phần 3.6.2.1 hay 3.6.2.3 là nó quy định, trong đó thứ tự các đối tượng tĩnh trong các đơn vị biên dịch khác nhau được khởi tạo nếu khởi tạo động được thực hiện trước tuyên bố đầu tiên của main.

xem xét như sau:

// consts.h 
#include <string> 

const std::string string1 = "ham"; 
const std::string string2 = "cheese"; 

// myclass.h 
#include <string> 

class MyClass 
{ 
public: 
    MyClass(); 
    MyClass(std::string str); 
    std::string Get() { return memberString; } 
private: 
    std::string memberString; 
} 

// myclass.cpp 
#include "consts.h" 
#include "myclass.h" 

MyClass::MyClass() : memberString(string1) {} 

MyClass::MyClass(std::string str) : memberString(str) {} 

// main.cpp 
#include <iostream> 
#include "consts.h" 
#include "myclass.h" 

MyClass myObject1; 
MyClass myObject2(string2); 

using namespace std; 

int main() 
{ 
    cout << myObject1.Get(); // might not print "ham" 
    cout << myObject2.Get(); // will always print "cheese" 
} 

Kể từ myclass.cpp có bản sao riêng của const biến, những có thể không được khởi tạo khi được gọi.

Vì vậy, có, const biến được định nghĩa trong tập tin tiêu đề có thể được sử dụng theo cách đó là dễ bị khởi thất bại tĩnh

Theo như tôi có thể thấy điều này không chỉ áp dụng cho các biến không cần khởi tạo tĩnh:

Từ C++ 03 tiêu chuẩn, phần 3.6.2.1:

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

12

Định nghĩa đầu tiên của bạn đặt path1 trong mỗi đơn vị biên dịch bao gồm config.h. Để tránh điều này, không xác định các biến trong tệp tiêu đề. Thông thường, bạn muốn khai báo các biến trong tiêu đề như extern:

extern const std::string path1; 
extern const MyClass myclass1; 

và xác định chúng trong một tập tin thực hiện, ví dụ config.cpp:

const std::string path1 = "/xyz/abc"; 
const MyClass myclass1("test"); 

Đôi khi bạn cần biến cố định chỉ có thể sử dụng từ một tệp triển khai. Sau đó, bạn có thể khai báo biến đó ở phạm vi tệp là static.

static const std::string path1 = "/xyz/abc"; 

static không được dùng nữa. staticextern đôi khi được ngụ ý, nhưng tôi luôn quên nơi và cách thức, vì vậy tôi thường chỉ định chúng rõ ràng cho tất cả các biến cấp không gian tên.

+1

đó là không đúng sự thật rằng 'static' chỉ có thể được sử dụng trong các tập tin thực hiện. –

+0

tất nhiên, không có khái niệm về tệp thực hiện trong C++, tôi sẽ cố gắng thay đổi từ ngữ – Philipp

+2

Tôi nghĩ cụm từ bạn đang tìm kiếm là "đơn vị dịch" thay vì tệp triển khai. Giá trị phải được khai báo trong tiêu đề và được xác định trong nhiều nhất một đơn vị dịch. Trong trường hợp này, vì chúng là const và ngụ ý liên kết nội bộ, bạn không nhận được lỗi biểu tượng được định nghĩa nhân tại thời gian liên kết, nhưng bạn có cùng biểu tượng được định nghĩa trong mọi đơn vị dịch bao gồm tiêu đề này. Vì chúng có liên kết nội bộ, chúng không phá vỡ liên kết, nhưng trình biên dịch có thể không loại bỏ các bản sao. Tôi một lần cạo 8 MB ra một thực thi bằng cách loại bỏ các định nghĩa của chuỗi từ tiêu đề. – legalize

8

Điểm được gọi là dấu cách khởi tạo tĩnh là một vấn đề khi một biến mức không gian tên phụ thuộc vào giá trị được gán cho một biến cấp không gian tên khác có thể hoặc không được khởi tạo trước đó. Trong hai ví dụ của bạn, không có sự phụ thuộc như vậy và không có bất kỳ vấn đề gì.

này, mặt khác, là dễ bị rằng loại lỗi:

// header.h 
extern const std::string foo; 

// constant.cpp 
const std::string foo("foo"); 

// main.cpp 
#include "header.h" 
const std::string foobar(foo+"bar"); 
int main() { 
    std::cout << foobar << std::endl; 
} 

Không có gì bảo đảm rằng foo sẽ được khởi tạo trước foobar, ngay cả khi cả hai đều không đổi. Điều đó có nghĩa rằng hành vi của chương trình là không xác định và nó cũng có thể in "foobar", "bar" hoặc chết.

+0

Tôi đã cập nhật trường hợp sử dụng của mình trong câu hỏi gốc. Đây chính là kịch bản mà tôi muốn biết. –

+0

Tôi sẽ không dám bình luận về điều đó. Suy nghĩ đầu tiên của tôi là nó phải ổn (hằng số là cục bộ cho đơn vị dịch, và được định nghĩa trước hằng số thứ hai), nhưng tôi sẽ làm nếu có thể tránh được cấu trúc đó. Cách đơn giản nhất mà tôi biết chắc chắn là sử dụng một hàm có biến tĩnh bên trong thay cho var toàn cục: 'inline myclass & global_object() {instance myclass; return instance; } 'Ngôn ngữ đảm bảo rằng' instance' sẽ được khởi tạo đầy đủ trong lời gọi đầu tiên tới hàm, vì vậy 'std :: string other (global_object(). Get());' chắc chắn sẽ làm việc ... –

+2

Ở mức nào, Tôi sẽ * thực sự * cố gắng tránh nó hoàn toàn và loại bỏ các biến mức không gian tên với khởi tạo của họ. Việc khởi tạo các biến mức không gian tên là không nhỏ, nó xảy ra trong hai lần, tất cả các * globals * được khởi tạo từ các hằng số nhận giá trị đầu tiên, và sau đó trong lần thứ hai vượt qua mọi thứ phụ thuộc vào một hằng số sẽ được khởi tạo theo thứ tự của định nghĩa (trong cùng một đơn vị dịch), theo thứ tự không xác định khi có nhiều đơn vị dịch được liên kết vào một chương trình. –

2

Dấu cách khởi tạo tĩnh liên quan đến biến tĩnh phụ thuộc vào lẫn nhau. Chỉ xác định một số biến số static const sẽ không phải là một vấn đề.

Các vấn đề liên quan