在C++中,通过引用和通过值将向量传递到函数中

Passing vectors into function by reference vs by value in C++

本文关键字:向量 函数 C++ 引用      更新时间:2023-10-16

我一直在写一个小项目,只是为了扩展我对c++的了解,但我遇到了一个问题。当我接受一个用户名时,我想检查它是否已经被占用了。如果使用了特定的用户名,我会将问题重新打印给用户。它在第一个循环中运行良好,但在那之后,它将接受任何东西,即使它确实存在于用户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 messagesn" : "User has 1 messagen");
43 
44 }

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

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

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

我强烈建议尽可能使用C++功能而不是C语言,因为它通常更可读,不易出错。

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

User类进行动态分配,但不定义复制构造函数和赋值运算符。对于显示问题的简单片段,请查看此处的片段。它只复制地址,而不是指向的实际数据,所以一旦第一个副本被删除,所有其他副本都无效。

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

还有一个原因是字符串文字在C++中是const char[],事实上在C中它们是char[],但不允许编辑它们,所以它实际上也是常量。这是因为编译器可以将字符串文字放入可执行文件的只读位置。因此,与其将字符串文字强制转换为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++自动处理所有的复制和删除逻辑,您不必处理烦人的指针内容。

在我做了最小的更改之后,它看起来工作得很好,所以它实际上是编译的。我只是使用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;
}

当我在ideone上运行这个程序,输入"foo"三次,然后输入"Greg"时,它会在每个"foo"之后不断提示,然后接受"Greg"。

由于用另一个类代替看不见的"User"类可以使其工作,所以问题一定出在该类中,可能是在处理传递给其构造函数的指针时。

一些注意事项:

  • 不需要将字符串文字"foo"answers"bar"强制转换为(char*);它们已经是CCD_ 21。如果你这样做是为了摆脱const-ness,不要这样做:它们可能存在于只读内存中。

  • 如果您有C++11,verify中的循环可以替换为的范围

    for (auto u : b) {
    if (strcmp(a, u.getUsername()) == 0)
    return false;
    }
    
  • 事实上,您可以消除整个函数verify并使用std::any_of(users.begin(), users.end(), [username](const User& u){ return strcmp(u.getUsername(), username) == 0; }),如果username已经在向量users中,则返回true

因此,一个更短、等效的完整程序是

#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;
}

(在视频上)