Làm thế nào để có thể khai báo không có gì bên trong main () trong C ++ mà vẫn có một ứng dụng hoạt động sau khi biên dịch?


86

Trong một cuộc phỏng vấn, tôi đã phải đối mặt với một câu hỏi như sau:

Bạn của bạn đã cung cấp cho bạn một tệp mã nguồn duy nhất in các số Fibonacci trên bảng điều khiển. Lưu ý rằng khối main () trống và không có bất kỳ câu lệnh nào bên trong nó.

Giải thích cách có thể làm được điều này (gợi ý: global instance!)

Tôi thực sự muốn biết về điều này, làm thế nào một điều như vậy lại có thể xảy ra!


26
Nhìn vào gợi ý!
R. Martinho Fernandes

14
Bởi vì đó là thứ mà 1) tôi chưa từng nghe nói đến, 2) là câu đố hữu ích vì mọi người hỏi nó trong các cuộc phỏng vấn, 3) một ứng dụng thú vị của ngôn ngữ cần biết để 4) Tôi có thể nhận ra nó và đâm vào mặt bất cứ ai một chiếc shiv gỉ nếu tôi thấy họ thực sự sử dụng nó trong mã sản xuất.
OmnipotentEntity

4
Một lập trình viên C ++ chuyên nghiệp, có năng lực sẽ biết câu trả lời cho câu hỏi này. Nếu mục đích của câu hỏi phỏng vấn này là để xác định xem người được phỏng vấn có phải là một lập trình viên C ++ chuyên nghiệp, có năng lực hay không, thì câu hỏi không nên cung cấp cho họ câu trả lời.
John Dibling

1
Trong cài đặt phỏng vấn, một cách thay thế sẽ là có logic bên trong bất kỳ hàm nào trong mã và ghi đầu ra bằng cách sử dụng asserthoặc #pragma messagev.v. Điều này sẽ chuyển hướng đầu ra đến bảng điều khiển trong quá trình biên dịch. Chương trình thậm chí có thể không bao giờ biên soạn đầy đủ, nhưng đây chắc chắn là một cách thú vị để thể hiện tư duy "vượt trội" của bạn trong cuộc phỏng vấn. Điều này thỏa mãn câu hỏi được trích dẫn vì nó KHÔNG đề cập bất cứ điều gì về việc tạo nhị phân; đúng hơn nó chỉ nói về một tệp C có thể hiển thị "thứ" trên bảng điều khiển. ;-)
TheCodeArtist

1
Đó có phải là một cuộc phỏng vấn cho IOCC ? :-) Ok, tôi thừa nhận là tôi thường làm việc đó để khởi tạo các nhà máy của mình hoặc thực thi một số mã kiểm tra. Btw, ' tệp mã nguồn duy nhất ' cũng là một gợi ý, rằng entry-pint (chính theo mặc định) không được thay thế bằng trình liên kết.
Valentin Heinitz

Câu trả lời:


127

Nó rất có thể được triển khai dưới dạng (hoặc một biến thể của nó):

 void print_fibs() 
 {
       //implementation
 }

 int ignore = (print_fibs(), 0);

 int main() {}

Trong đoạn mã này, biến toàn cục ignorephải được khởi tạo trước khi nhập vào main()hàm. Bây giờ để khởi tạo toàn cục, print_fibs()cần được thực thi ở nơi bạn có thể làm bất cứ điều gì - trong trường hợp này, hãy tính toán các số fibonacci và in chúng! Một điều tương tự mà tôi đã trình bày trong câu hỏi sau (mà tôi đã hỏi từ lâu):

Lưu ý rằng mã như vậy không an toàn và tốt nhất nên tránh nói chung. Ví dụ, std::coutđối tượng có thể không được khởi tạo khi print_fibs()được thực thi, nếu vậy thì sẽ std::coutlàm gì trong hàm? Tuy nhiên, nếu trong các trường hợp khác, nó không phụ thuộc vào thứ tự khởi tạo như vậy, thì có thể an toàn để gọi các hàm khởi tạo (đây là một thực tế phổ biến trong C và C ++).


3
@Nawaz Có lẽ đáng để trích dẫn những lời đảm bảo chính xác. Các đối tượng trong một đơn vị dịch được đảm bảo sẽ được khởi tạo theo thứ tự. Các đối tượng dòng tiêu chuẩn được đảm bảo sẽ được khởi tạo trước hoặc trong lần khởi tạo đầu tiên của một std::ios_base::Initđối tượng. Và <iostream>được đảm bảo hoạt động "như thể" nó chứa một thể hiện của một std::ios_base_Initđối tượng ở phạm vi không gian tên.
James Kanze

3
@ Steve314: Nó không trả về bất cứ thứ gì, đó là lý do tại sao tôi đã sử dụng toán tử dấu phẩy, để đảm bảo rằng kiểu của toàn bộ biểu thức (print_fibs(), 0)int. Đây là Demo trực tuyến .
Nawaz

1
@Nawaz Một thay thế cho hàm void và toán tử dấu phẩy sẽ trả về a boolvà biến bool fibsPrinted. Điều đó có thể rõ ràng hơn một chút nếu chức năng chỉ phục vụ ở đây. (Tuy nhiên, sự khác biệt có lẽ là không đủ để lo lắng về.)
James Kanze

1
+1, Nói về tuyệt vời. Phải tham gia stackoverflow chỉ để tán thành câu hỏi này và câu trả lời này.
Điểm cố định

1
@Nawaz Tôi không chắc ý của bạn là gì. Định nghĩa của std::coutlà một nơi nào đó trong thư viện. Nhưng như tôi đã chỉ ra, tiêu chuẩn yêu cầu nó phải được khởi tạo trước khi phương thức khởi tạo đầu tiên của một std::ios_base::Initđối tượng kết thúc và nó yêu cầu việc bao gồm <iostream>hoạt động như thể một std::ios_base::Initđối tượng được xác định ở phạm vi không gian tên. Nếu đơn vị dịch bao gồm <iostream>trước định nghĩa của đối tượng được khởi tạo, std::coutđược đảm bảo sẽ được xây dựng.
James Kanze

18

Hi vọng điêu nay co ich

class cls
{
  public:
    cls()
    {
      // Your code for fibonacci series
    }
} objCls;

int main()
{
}

Vì vậy, ngay sau khi một biến toàn cục của lớp được khai báo, hàm tạo được gọi và ở đó bạn thêm logic để in ra chuỗi Fibonacci.


9

Có nó là có thể. Bạn cần khai báo một phiên bản toàn cục của một đối tượng tính toán các số Fibonacci trong hàm tạo đối tượng.


6
Bạn cần khai báo một phiên bản toàn cục của một đối tượng có trình khởi tạo tính toán các số Fibonacci.
James Kanze

4

Tôi biết một số ví dụ như bạn kể. Một cách để có được nó là sử dụng lập trình ẩn mẫu. Sử dụng nó, bạn có thể chuyển một số quy trình tính toán sang biên dịch.

Ở đây bạn có thể lấy một ví dụ với các số Fibonacci

Nếu bạn sử dụng nó trong hàm tạo lớp tĩnh và bạn có thể viết các số mà không cần viết bất kỳ mã nào trong hàm main.

Hy vọng nó sẽ giúp bạn.


3

Mọi thứ có thể xảy ra trong quá trình khởi tạo các biến toàn cục / tĩnh. Mã sẽ được kích hoạt khi bắt đầu ứng dụng.


3

Tất cả các hàm tạo [*] cho các đối tượng phạm vi tệp được gọi trước khi đến main, cũng như tất cả các biểu thức khởi tạo cho các biến phạm vi tệp không phải đối tượng.

Chỉnh sửa: Ngoài ra, tất cả các hàm hủy [*] cho tất cả các đối tượng phạm vi tệp được gọi theo thứ tự xây dựng ngược lại sau khi mainthoát. Về mặt lý thuyết, bạn có thể đặt chương trình fibonacci của mình vào trình hủy của một đối tượng.

[*] Lưu ý rằng 'tất cả' bỏ qua hành vi tải và dỡ động các thư viện mà chương trình của bạn không được liên kết trực tiếp. Tuy nhiên, về mặt kỹ thuật, những ngôn ngữ này nằm ngoài ngôn ngữ C ++ cơ bản.


Tất cả ? Ngay cả những người trong dll được tải rõ ràng sau main?
James Kanze

Chà, về mặt kỹ thuật C ++ không định nghĩa các thư viện được tải động, vì vậy trong C ++ thuần túy, câu lệnh của tôi là đúng. Vì vậy, đánh bóng nó "Tất cả, lưu cho các trình khởi tạo và các đối tượng phạm vi tệp có trong DLL / DSO được tải sau khi đến main." Trong trường hợp này, mainlà trống, vì vậy các DLL / DSO đó sẽ phải được tải bởi các trình hủy, điều này thật sai lầm. Nhưng, đây là khoa học máy tính, tôi cho rằng chúng ta nên cẩn thận với những từ như "tất cả".
Joe Z

Tôi đã thêm một cảnh báo về 'tất cả' vào câu trả lời của mình ở trên và cũng thêm một ghi chú về dtors.
Joe Z

Có, nhưng hy vọng điều đó sẽ đến. Pre C ++ 11 chứa một số từ ngữ chồn có nghĩa là cho phép các DLL, nhưng trên thực tế chỉ có nghĩa là về mặt kỹ thuật, bảo đảm không phải lúc nào cũng ở đó, mặc dù nó có trong tất cả các triển khai thực tế và nhiều mã phụ thuộc vào nó. C ++ 11 đã khắc phục điều đó, ít nhất.
James Kanze
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.