As long as User does not own the Job and Job does not own the User then this is fine.
For example, perhaps you create collections of Jobs and Users up-front and then want to create associations between them:
#include <string>
#include <vector>
class Job;
class User {
std::string user_name;
Job* job;
public:
explicit User(const std::string& user_name) : user_name(user_name) {}
void setJob(Job& job) { this->job = &job; }
};
class Job {
std::string job_type;
int salary;
User* user;
public:
Job(const std::string& job_type, int salary) : job_type(job_type), salary(salary) {}
void setUser(User& user) { this->user = &user; }
};
void recruit(User& user, Job& job) {
user.setJob(job);
job.setUser(user);
}
int main() {
auto jobs = std::vector<Job>{ {"Tinker", 10'000}, {"Tailor", 20'000}};
auto users = std::vector<User> {User{"George"}, User{"Percy"}};
recruit(users[0], jobs[1]);
recruit(users[1], jobs[0]);
}
As long as the collections of Users and Jobs are destroyed at the same time there is no danger of dangling pointers. It might be preferable if the pointers were const pointers.
But if your intention is some sort of ownership then a smart pointer would be preferred.