在 C++ 中通过引用与按值将向量传递给函数

Posted

技术标签:

【中文标题】在 C++ 中通过引用与按值将向量传递给函数【英文标题】:Passing vectors into function by reference vs by value in C++ 【发布时间】:2016-05-28 21:57:44 【问题描述】:

我一直在编写一个小项目,只是为了扩展我对 c++ 的了解,但遇到了一个问题。当我接受用户名时,我想检查它是否已经被占用。如果使用了该特定用户名,我会将问题重新打印给用户。它在第一个循环中运行良好,但之后它会接受任何内容,即使它确实存在于 users Vector 中。

     bool verify(char * a, vector<User> b)
 14         for(int i = 0; i < b.size(); i++)
 15                 if(strcmp(a, b[i].getUsername()) == 0)
 16                         return false;
 17                 
 18         
 19         return true;
 20 
 21 
 22 int main()
 23 
 24         vector<User> users;
 25 
 26         User us1((char *)"foo", (char *)"bar");
 27         users.push_back(us1);
 28 
 29         
 30         do
 31                 cout << "Enter Username: ";
 32                 scanf(" %s", username);
 33                 
 34                 
 35          while(!verify(username, users));
 36         
 37         return 0;
 38    

但是,如果我的函数 verify 取而代之的是一个向量 & b 它工作正常。有人可以解释为什么会这样吗?

用户.cpp

    User:: User(char * userName, char * passWord)
  9 
 10         this->userName = strdup(userName);
 11         this->passWord = strdup(passWord);
 12 
 13 
 14 
 15 User:: ~User()
 16 
 17         delete userName;
 18         delete passWord;
 19 
 20 
 21 
 22 void User::getMessage()
 23 
 24         cout << message << endl;
 25 
 26 
 27 char * User:: getUsername()
 28 
 29         return userName;
 30 
 31 
 32 char * User :: getPassword()
 33 
 34         return passWord;
 35 
 36 
 37 void User:: printUser()
 38 
 39         cout << "User Information" << endl;
 40         cout << "Username: "<< userName << endl;
 41         cout << "Password: "<< passWord << endl;
 42         cout << "Messages: "<< ((message == NULL) ? "User has no messages\n" : "User has 1 message\n");
 43 
 44 

【问题讨论】:

如果你在 C++ 中使用strcmp,那你就错了。 std::string 是你的朋友。 我的猜测是 User 包含 char * 并且您违反了三规则。但由于您尚未发布User 的代码,您的问题目前无法回答。发布minimal reproducible example。 你使用cout,也使用cin。这里没有声明变量username User 的复制构造函数很可能在某些方面是错误的。否则,您的代码应该以任何一种方式工作,尽管按值传递容器往往是一个性能问题。但是,您没有显示 User 类的复制构造函数。 1.显示minimal reproducible example,而不是随机的一小部分代码,如果某些东西不工作,不显示大部分不工作的代码是不合逻辑的,请参阅minimal reproducible example 帮助文章了解更多信息。 2. 编写真正的 C++ 代码,而不是伪装成 C++ 的 C 代码。 strcmp()、scanf()等,在真正的C++代码中出现没有业务。 【参考方案1】:

除了使用std::string 的所有讨论之外,OP 代码中的基本问题是User 类缺少用户定义的复制构造函数。默认的只会复制userNamepassWord 字段中的值,导致两个向量(main 中的一个和为verify 函数创建的一个)指向相同的分配内存地址。当verify 返回时,该内存被删除,在main 的向量中留下Users 和悬空指针(指向释放的内存)。

使用引用来避免这种删除并保持原始向量不变。

这就是你现在不应该在代码中使用原始指针的原因之一。

【讨论】:

谢谢!这有帮助。我对此很陌生,显然还没有对使用字符串而不是字符数组之类的东西进行完整的实验【参考方案2】:

我强烈建议尽可能使用 C++ 功能而不是 C 东西,它通常更具可读性且不易出错。

现在你的代码中有多个问题,@AlanStrokes' comment 是对的,你的主要问题是没有正确处理三规则。

您的User 类执行动态分配,但未定义复制构造函数和赋值运算符。有关显示问题的简单 sn-p,请查看 sn-p here。它只复制地址,而不是指向的实际数据,因此一旦第一个副本被删除,所有其他副本都无效。

您还有其他问题,strdup 是不可移植的,它不是 C 标准的一部分。它是 POSIX 标准的一部分,因此很可能仅在实现该标准的系统上可用。它也是一个用malloc分配内存的C函数,你应该删除它从C中返回free的指针,而不是从C++中返回的delete

还有一个原因,字符串文字在 C++ 中是 const char[],实际上在 C 中它们是 char[],但你不能编辑它们,所以它实际上也是 const 。这是因为允许编译器将字符串文字放入可执行文件的只读位置。因此,不要将字符串文字转换为char *,而是让您的函数正确地采用const char *


所有所说的处理这些指针的事情都很烦人,因为 C++ 使用 std::string 让事情变得更容易,我建议改用它:

class User 
  private:
    std::string userName;
    std::string password;

    User(const std::string &userName, const std::string & passWord);
    std::string getUsername();
    std::string getPassword();
    void printUser();
;

User::User(const std::string & userName, const std::string & passWord) 
  this->userName = userName;
  this->passWord = passWord;


std::string User::getUsername() 
  return userName;


std::string User::getPassword() 
  return passWord;


// etc...

这里 C++ 自动处理所有的复制和删除逻辑,您不必处理烦人的指针问题。

【讨论】:

【参考方案3】:

在我进行了最小的更改以使其实际编译之后,它似乎工作正常。 我只是使用 std::string 来存储字符串,而不是猜测您的 User 类。

#include <vector>
#include <string>
#include <iostream>
#include <cstdio>
#include <cstring>

struct User 
    std::string name, game;
    User(const char* nam, const char* gam) : name(nam), game(gam) 
    const char* getUsername() const 
        return name.c_str();
    
;

bool verify(char * a, std::vector<User> b) 
    for (int i = 0; i < b.size(); i++) 
        if (std::strcmp(a, b[i].getUsername()) == 0) 
            return false;
        
    
    return true;


int main()    
    std::vector<User> users;

    User us1("foo", "bar");
    users.push_back(us1);

    char username[100];

    do 
        std::cout << "Enter Username: ";
        std::scanf(" %50s", username);
     while(!verify(username, users));

    return 0;

当我运行这个on ideone,输入“foo”三遍,然后输入“Greg”,它会在每个“foo”之后不断提示,然后接受“Greg”。

由于用另一个类代替你看不见的“用户”类使其工作,问题一定出在那个类上,可能在你处理传递给它的构造函数的指针上。

一些注意事项:

应该不需要将字符串文字“foo”和“bar”转换为(char*);他们已经是char*。如果您这样做是为了摆脱 const-ness,请不要:它们可能驻留在只读内存中。

如果你有 C++11,verify 中的循环可以替换为 range-for:

for (auto u : b) 
    if (strcmp(a, u.getUsername()) == 0)
        return false;

1234563

所以一个更短的等价完整程序是

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

struct User 
    std::string name, game;
    User(const char* nam, const char* gam) : name(nam), game(gam) 
    const std::string& getUsername() const 
        return name;
    
;

int main()
    std::vector<User> users;
    users.push_back("foo","bar");
    std::string username;

    do 
        std::cout << "Enter Username: ";
        std::cin >> username;
     while(std::any_of(users.begin(), users.end(),
            [username](const auto& u) return u.getUsername() == username; ));

    return 0;

(on ideone)

【讨论】:

以上是关于在 C++ 中通过引用与按值将向量传递给函数的主要内容,如果未能解决你的问题,请参考以下文章

如何访问在 C++ 中通过引用传递的列表/向量的元素

哪个更快?按引用传递与按值传递 C++

在 C++ 中通过引用/值传递

在c ++中通过引用返回类的向量[重复]

在python中通过引用传递引用

java的按值传递与按引用传递