从 C# 切换到 C++。我的代码有什么问题吗?我需要标题来完成我想要做的事情吗?一个文件中的类定义问题

发布于 2024-12-28 13:57:54 字数 2809 浏览 2 评论 0原文

我用 C# 编程已有几年了,因为它是我的第一语言。我正在尝试温习我的 C++,因为我很快就会开发一些用 C++ 编码的东西。

这段代码有什么问题:(我知道可能有很多问题。C++ 与 C# 的需求有很大不同)。有人告诉我,我不知道如何在 C++ 中正确声明类,并且我需要使用标头来定义我的类。我需要标题吗?这是一个只是为了测试的小程序,想知道如果没有它是否可以完成。缺少的标头是这里唯一的问题吗?我遇到了无法访问 Company 中的 Parse 的错误,但是当我在 Company 类名前面添加 public 时,它会引发更多错误。

啊!太令人沮丧了。

#include "std_lib_facilities.h"
using namespace std;

class Employee
{
public:
    string screen_name;
    string real_name;
    string current_job;
    int employee_number;
    Employee(int no, string name1, string name2, string current_jobin)
    {
        screen_name=name1;
        real_name=name2;
        employee_number=no;
    current_job=current_jobin;
    }
};

class Project
{
public:
    Vector<Employee> Employees;
    int max_worker_quota;
    int project_id;
    string project_name;
    Project(int no_in,int max_in,string title_in)
    {
        max_worker_quota=max_in;
        project_name=title_in;
        project_id=no_in;
    }
};

unsigned int split(const std::string &txt, vector<std::string> &strs, char ch)
{
    unsigned int pos = txt.find( ch );
    unsigned int initialPos = 0;
    strs.clear();

    // Decompose statement
    while( pos != std::string::npos ) {
        strs.push_back( txt.substr( initialPos, pos - initialPos + 1 ) );
        initialPos = pos + 1;

        pos = txt.find( ch, initialPos );
    }

    // Add the last one
    strs.push_back( txt.substr( initialPos, std::min( pos, txt.size() ) - initialPos + 1));

    return strs.size();
}

class Company
{
Vector<Employee> Employeelist;
Vector<Project> Projectlist;

    void Parse(string input)
    {
        //Case Statements
        vector<string> temp;
        split( input, temp, ' ' );

        if (temp[0]=="S")
        {
            //Add Employee to Company
            Employee myEmployee=Employee(atoi(temp[1].c_str()),temp[2],temp[3],temp[4]);
            Employeelist.push_back(myEmployee);
        }
        else if (temp[0]=="C")
        {
            //Add Project to Company
            Project myProject=Project(atoi(temp[1].c_str()),atoi(temp[2].c_str()),temp[3]);
            Projectlist.push_back(myProject);
        }
        else if (temp[0]=="L")
        {
            //Add Employee to Project list
            //Not Implemented-Find Project by temp[1] which is a int
        }
        else if (temp[0]=="A")
        {
        }
        else if (temp[0]=="D")
        {
        }
        else if (temp[0]=="PS")
        {
        }
        else if (temp[0]=="PC")
        {
        }
    }
};

int main(int argc, char *argv[])
{
string input;
cout<<"Command:: ";
cin>>input;
Company myCompany;
myCompany.Parse(input); //Input is in the format X Name Name etc etc. Arguments separated by spaces

return 0;
}

I've been programming in C# for a few years now, as it was my first language. I'm trying to brush up on my c++ because I will be working on something soon that is coded in that.

What is wrong with this code: (I know there might be a lot of things wrong. C++ is so different than C# in what it needs). Someone told me that I don't know how to declare classes correctly in C++, and that I need to define my classes using headers. Do I NEED headers? This is a small program just to test and would like to know if this can be accomplished without it. And are the missing headers the only issue here? I had a error about not being able to access Parse in Company, but when I add public in front of Company class name, it throws more errors.

AUGH! So frustrating.

#include "std_lib_facilities.h"
using namespace std;

class Employee
{
public:
    string screen_name;
    string real_name;
    string current_job;
    int employee_number;
    Employee(int no, string name1, string name2, string current_jobin)
    {
        screen_name=name1;
        real_name=name2;
        employee_number=no;
    current_job=current_jobin;
    }
};

class Project
{
public:
    Vector<Employee> Employees;
    int max_worker_quota;
    int project_id;
    string project_name;
    Project(int no_in,int max_in,string title_in)
    {
        max_worker_quota=max_in;
        project_name=title_in;
        project_id=no_in;
    }
};

unsigned int split(const std::string &txt, vector<std::string> &strs, char ch)
{
    unsigned int pos = txt.find( ch );
    unsigned int initialPos = 0;
    strs.clear();

    // Decompose statement
    while( pos != std::string::npos ) {
        strs.push_back( txt.substr( initialPos, pos - initialPos + 1 ) );
        initialPos = pos + 1;

        pos = txt.find( ch, initialPos );
    }

    // Add the last one
    strs.push_back( txt.substr( initialPos, std::min( pos, txt.size() ) - initialPos + 1));

    return strs.size();
}

class Company
{
Vector<Employee> Employeelist;
Vector<Project> Projectlist;

    void Parse(string input)
    {
        //Case Statements
        vector<string> temp;
        split( input, temp, ' ' );

        if (temp[0]=="S")
        {
            //Add Employee to Company
            Employee myEmployee=Employee(atoi(temp[1].c_str()),temp[2],temp[3],temp[4]);
            Employeelist.push_back(myEmployee);
        }
        else if (temp[0]=="C")
        {
            //Add Project to Company
            Project myProject=Project(atoi(temp[1].c_str()),atoi(temp[2].c_str()),temp[3]);
            Projectlist.push_back(myProject);
        }
        else if (temp[0]=="L")
        {
            //Add Employee to Project list
            //Not Implemented-Find Project by temp[1] which is a int
        }
        else if (temp[0]=="A")
        {
        }
        else if (temp[0]=="D")
        {
        }
        else if (temp[0]=="PS")
        {
        }
        else if (temp[0]=="PC")
        {
        }
    }
};

int main(int argc, char *argv[])
{
string input;
cout<<"Command:: ";
cin>>input;
Company myCompany;
myCompany.Parse(input); //Input is in the format X Name Name etc etc. Arguments separated by spaces

return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尽揽少女心 2025-01-04 13:57:55

首先,您不需要用于测试目的的标头。但是没有它们你就无法编写真正的程序,因为标头定义了单独编译的程序部分的接口。这就是 C/C++ 的工作方式,没有办法绕过。

其次,您必须将 public: 添加到您的 Company 类中并处理以下错误。它就像 C# 的东西:您必须定义一个函数 public void Parse(string) 才能从类外部访问它。 C++ 方法是

class Foo { 
public:
void Bar();
}; 

第三种,在 C++ 中在类定义中定义非平凡函数是非常规的(唯一的例外是模板类)。这就是标题故事的另一面。

好的,这是与标头相关的基本内容的简要说明。

程序通常分为一组单独编译的文件,即翻译单元。每个单元通常由一个 .cpp 文件和一个或多个头文件 (.h) 组成。编译这些文件后,您将获得一个二进制 .obj 文件。该文件包含对象 - 初始化全局(和命名空间)对象所需的函数和内容的代码。要制作一个程序,您需要将一个或多个目标文件传递给链接器。在 VS 中,它发生在幕后。您只需将 .cpp 文件添加到项目树中,IDE 将相应地配置项目依赖项。

您的代码可能如下所示:

//File: employee.h

#ifndef EMPLOYEE_HDR  /* include guard */
#define EMPLOYEE_HDR

#include <string>

using std::string;

class Employee {
public:
Employee (int no, string name1, string name2, string current_jobin);

void PayBonus(int amount);

void Fire(string reason);

int GetSalary() const
{ return m_Salary; }

/*...*/
protected:
int m_Salary;
string m_FirstName;
/* ... */
};

#endif

//File: employee.cpp
#include "employee.h"

Employee::Employee (int no, string name1, string name2, string current_jobin)
{
 //definition
}

void Employee::PayBonus(int amount)
{
 //definition
}

void Employee::Fire(string reason)
{
//definition
}

/* define other non-trivial class functions here */

//File: company.h

#ifndef COMPANY_HDR  /* include guard */
#define COMPANY_HDR

#include <vector>
using std::vector;

#include "employee.h"

class Company {
public:
Company();
void Hire(string name);

void WorldCreditCrunch() //life is unfair
{ FireEveryone(); }

void Xmas(); //pays $5 bonus to everyone

/* ... */

protected:

vector<Employee> m_Staff;

void FireEveryone();

/* ... */
};

#endif

//File: company.cpp

#include "company.h"

Company::Company()
{
 //definition
}

void Company::Hire(string name)
{
     //calculate new employee ID etc
     m_Staff.push_back(Employe( /*...*/));
}

void Company::FireEveryone()
{
    for(size_t i = 0; i < m_Staff.size(); ++i)
         m_Staff[i].Fire();

}

void Company::Xmas()
{
    for(size_t i = 0; i < m_Staff.size(); ++i)
         m_Staff[i].PayBonus(5);
}

/* ... */

//File: main.cpp

#include "company.h"

int main()
{
     Company c;

     c.Hire("John Smith");

     /* ...*/

     return 0;
}

因此,基本上我们将拥有 employeecompanymain 单位。 employee.h 中 Employee 类的定义包含重要的函数声明。像 GetSalary() 这样的简单函数就在类内部定义。它向编译器提示内联它。 employee.cpp 包含其余的函数定义;

company.h 文件具有 #include "employee.h" 预处理器语句。因此,我们可以在类定义及其实现文件 (company.cpp) 中使用 Employee 对象。

main.cpp 包含程序入口点。它能够使用 Company 类,因为它包含“company.h”。

如果我们更改 Employee::Hire() 函数实现中的某些内容,则只有 employee.obj 会被重新编译。这是这样的节目组织的主要目的。但是,如果我们更改 Employee 接口(employee.h 中的类定义),每个程序单元都需要重新编译。

如果您执行以下操作,则需要包含防护:

#include "employee.h"
#include "company.h"  /* will contain the 2nd inclusion of employee.h which will be 
                        prevented by include guard */

基于 Microsoft Visual C++ 的项目通常使用 #pragma Once 来达到相同目的。它更容易使用,但通常不便携。

例如,如果您将 Employee::Hire 定义放入 employee.h 中,编译器会将函数代码放入 employee.obj 和 company.obj 中(因为 company.h< /code> 包括 employee.h)。当您尝试在这种情况下链接时,链接器将遇到同一功能代码的 2 个版本,并会给出错误。内联函数不会编译成单独的实体,因此不会导致此类错误。模板代码也是如此,仅在实例化模板时生成。因此,多个翻译单元可能具有相同非内联模板函数的代码。

由程序员来定义程序的各部分边界。如果需要,您可以将公司和员工放入单个翻译单元中。 VS 向导倾向于为每个主要类创建一个 .h/.cpp 对。尝试做一个MFC项目,自己看看。

这些是基础知识。正如我在上面的评论中提到的,您可以从 Stroustrup 的《C++ 编程语言》中全面了解此类内容

First of all, you don't need headers for test purposes. But you can not make a real program without them, because the headers define the interface of separately compiled program parts. That's the way the C/C++ work, no way around.

Second, you have to add public: to your Company class and deal with the following errors. It is just like the C# stuff: you have to make a function defined public void Parse(string) to be able to access it from outside the class. The C++ way is

class Foo { 
public:
void Bar();
}; 

Third, it is unconventional in C++ to define non-trivial functions inside class definition (the only exception being template classes). That's the other side of the headers story tho.

OK, here is a brief explanation of basic header-related stuff.

Program is usually divided into the set of separately compiled files, that is to say translation units. Each unit usually consist of one .cpp file and one or more header files (.h). When these files are compiled you are getting a single binary .obj file. This file contains objects - the code for your functions and stuff needed to initialize global (and namespace) objects. To make a program you need to pass one or more object files to linker. In VS it happens behind the scene. You just add a .cpp file to your project tree and IDE will configure the project dependencies accordingly.

This is how your code may look like:

//File: employee.h

#ifndef EMPLOYEE_HDR  /* include guard */
#define EMPLOYEE_HDR

#include <string>

using std::string;

class Employee {
public:
Employee (int no, string name1, string name2, string current_jobin);

void PayBonus(int amount);

void Fire(string reason);

int GetSalary() const
{ return m_Salary; }

/*...*/
protected:
int m_Salary;
string m_FirstName;
/* ... */
};

#endif

//File: employee.cpp
#include "employee.h"

Employee::Employee (int no, string name1, string name2, string current_jobin)
{
 //definition
}

void Employee::PayBonus(int amount)
{
 //definition
}

void Employee::Fire(string reason)
{
//definition
}

/* define other non-trivial class functions here */

//File: company.h

#ifndef COMPANY_HDR  /* include guard */
#define COMPANY_HDR

#include <vector>
using std::vector;

#include "employee.h"

class Company {
public:
Company();
void Hire(string name);

void WorldCreditCrunch() //life is unfair
{ FireEveryone(); }

void Xmas(); //pays $5 bonus to everyone

/* ... */

protected:

vector<Employee> m_Staff;

void FireEveryone();

/* ... */
};

#endif

//File: company.cpp

#include "company.h"

Company::Company()
{
 //definition
}

void Company::Hire(string name)
{
     //calculate new employee ID etc
     m_Staff.push_back(Employe( /*...*/));
}

void Company::FireEveryone()
{
    for(size_t i = 0; i < m_Staff.size(); ++i)
         m_Staff[i].Fire();

}

void Company::Xmas()
{
    for(size_t i = 0; i < m_Staff.size(); ++i)
         m_Staff[i].PayBonus(5);
}

/* ... */

//File: main.cpp

#include "company.h"

int main()
{
     Company c;

     c.Hire("John Smith");

     /* ...*/

     return 0;
}

So, basically we gonna have employee, company and main units. The definition of Employee class in employee.h contains non-trivial functions declaration. A simple function like GetSalary() is defined right inside the class. It gives a hint to the compiler to inline it. The employee.cpp contains the rest of function definitions;

The company.h file has #include "employee.h" preprocessor statement. So we may use Employee objects in the class definition and in its implementation file (company.cpp).

The main.cpp contains the program entry point. It is able to use Company class cause it includes "company.h".

If we change something in the Employee::Hire() function implementation, only employee.obj will be recompiled. This is the main purpose of such program organization. But if we change the Employee interface (class definition in employee.h) every program unit will require recompilation.

Include guards are needed in case you do something like this:

#include "employee.h"
#include "company.h"  /* will contain the 2nd inclusion of employee.h which will be 
                        prevented by include guard */

Projects based on Microsoft Visual C++ often use #pragma once for the same purpose. It is easier to use but generally not portable.

If you put, for example, Employee::Hire definition in the employee.h the compiler will put the function code in both employee.obj and company.obj (cause the company.h includes employee.h). When you try to link in such situation the linker will encounter 2 versions of the same function code and will give an error. Inline functions are not compiled in into separate entities and thus don't cause such error. Same goes about template code which is generated only when the template is instantiated. So, several translation units may have code for the same non-inline template functions.

It is up to programmer to define the parts boundaries of the program. You may put Company and Employee into single translation unit, if you want. The VS wizards tend to make a .h/.cpp pair for each major class tho. Try to make an MFC project and see for yourself.

These are the basics. As I mentioned in the comment above, you can get a full picture of such stuff from Stroustrup's "The C++ programming language"

一百个冬季 2025-01-04 13:57:55

您可以使用 public 来开始类中的公共方法部分,而不是类本身。也就是说,您应该在 Company 类中的 Parse 方法之前添加 public:

class Company
{
  Vector<Employee> Employeelist;
  Vector<Project> Projectlist;

public:
  void Parse(string input)
  {

You use public to begin a section of public methods within the class, not on the class itself. That is, you should add public: before the Parse method within the Company class.

class Company
{
  Vector<Employee> Employeelist;
  Vector<Project> Projectlist;

public:
  void Parse(string input)
  {
以为你会在 2025-01-04 13:57:55

这只是为了扩展帕维尔自您在评论中提出要求以来的第三点。

他的意思基本上是这样的:在任何真正的 C++ 项目中,您都需要将类的定义与其实现分离成两个文件,一个头文件 (*.h) 和实现文件( *.cpp)。使用上面的示例,它可以像这样分开:

// Employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <string>

class Employee
{
public:
    Employee(int no, 
             std::string name1, 
             std::string name2, 
             std::string current_jobin);

private:
    std::string screen_name;
    std::string real_name;
    std::string current_job;
    int employee_number;    
};
#endif

// Employee.cpp
#include "Employee.h"

Employee::Employee(int no, 
                   std::string name1, 
                   std::string name2, 
                   std::string current_jobin)
{
    screen_name = name1;
    real_name = name2;
    employee_number = no;
    current_job = current_jobin;
}

// Company.h
#ifndef COMPANY_H
#define COMPANY_H
#include <vector>
#include <string>
#include "Employee.h"
#include "Project.h"

class Company
{
public:
    void Parse(std::string input);

private:
    std::vector<Employee> Employeelist;
    std::vector<Project>  Projectlist;
};
#endif

// Company.cpp
#include "Company.h"

void Company::Parse(std::string input)
{
    // your code for Parse in Company goes here
}

并且您的 main.cpp 将像这样使用上面的内容:

// main.cpp
#include <iostream>
#include <string>
#include "Company.h"

int main(int argc, char *argv[])
{
    std::string input;
    std::cout << "Command:: ";
    std::cin >> input;
    Company myCompany;
    //Input is in the format X Name Name etc etc. Arguments separated by spaces    
    myCompany.Parse(input); 
}

现在来自 C# 背景的您可能想知道为什么事情是这样完成的 - 毕竟您最终得到了文件数量是原来的两倍!简短的答案是因为 C 就是这样做的,而 C++ 继承了 C 的很多包袱

。较长的答案是因为 C++ 不使用像 C# 和 Java 那样的“模块”系统。构建源文件时,编译器除了正在编译的当前文件之外不会查看其他任何地方。因此,编译器了解您正在使用的类、函数或变量是否存在的唯一方法是,它是否在使用之前出现在正在编译的源文件中的某个位置。由 #include 指令引入的所有标头以及源 .cpp 的正文就是所谓的“编译单元”——它是一个独立的单元,包含编译器的所有内容需要知道成功编译而不需要在其他地方寻找。

This is just to expand on Pavel's third point since you requested in your comment.

What he means is basically this: in any real C++ project you'll want to separate out the definition of your class from its implementation into two files, a header file (*.h) and implementation file(*.cpp). Using your above example, it could be separated out like this:

// Employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <string>

class Employee
{
public:
    Employee(int no, 
             std::string name1, 
             std::string name2, 
             std::string current_jobin);

private:
    std::string screen_name;
    std::string real_name;
    std::string current_job;
    int employee_number;    
};
#endif

// Employee.cpp
#include "Employee.h"

Employee::Employee(int no, 
                   std::string name1, 
                   std::string name2, 
                   std::string current_jobin)
{
    screen_name = name1;
    real_name = name2;
    employee_number = no;
    current_job = current_jobin;
}

// Company.h
#ifndef COMPANY_H
#define COMPANY_H
#include <vector>
#include <string>
#include "Employee.h"
#include "Project.h"

class Company
{
public:
    void Parse(std::string input);

private:
    std::vector<Employee> Employeelist;
    std::vector<Project>  Projectlist;
};
#endif

// Company.cpp
#include "Company.h"

void Company::Parse(std::string input)
{
    // your code for Parse in Company goes here
}

and your main.cpp would use the above like this:

// main.cpp
#include <iostream>
#include <string>
#include "Company.h"

int main(int argc, char *argv[])
{
    std::string input;
    std::cout << "Command:: ";
    std::cin >> input;
    Company myCompany;
    //Input is in the format X Name Name etc etc. Arguments separated by spaces    
    myCompany.Parse(input); 
}

Now coming from a C# background you're probably wondering why things are done this way -- after all you end up with twice as many files! The short answer is because C did it this way and C++ inherited a lot of baggage from C.

The longer answer is because C++ doesn't use a 'module' system like what you'd find in C# and Java. When building a source file the compiler doesn't look anywhere else except for the current file it's compiling. So the only way the compiler will know about the existence of a class, function or variable that you're using is if it shows up somewhere in the source file being compiled prior to use. The inclusion of all the headers pulled in by the #include directive together with the body of the source .cpp is what's termed as a 'compilation unit' -- it's a self-contained unit containing everything the compiler needs to know to successfully compile without looking else where.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文