【问题标题】:C++ private access to other classes' membersC++ 对其他类成员的私有访问
【发布时间】:2012-02-18 07:38:55
【问题描述】:

我正在使用boost::asio(用于套接字)、boost::thread(用于线程)、libconfig++(用于读取配置文件)和protocol buffers(用于协议实现)编写一个多线程服务器。

服务器或多或少遵循这条路线:main() -> 创建一个应用程序对象 -> 运行应用程序对象。应用程序加载配置文件,然后创建服务器对象(作为 const 传递给配置类)。服务器对象配置自己并绑定端口,开始接受,等等。每当检测到新客户端时,服务器都会创建一个新的客户端对象,然后创建一个运行客户端连接处理程序的线程。

所有这些都是为了说明配置文件是从我的 Application 类加载的,然后一直传递到我的 Client 类。如果 libconfig 对象一直直接传递给客户端,这应该不会造成任何麻烦,但众所周知,多线程意味着当两个或多个线程同时访问时内存会损坏。

The way to solve this was discussed in other post 最终实现了一个自动解决互斥问题的包装器。

魔法课

app_config.h

#ifndef _APP_CONFIG_H_
#define _APP_CONFIG_H_ 1

#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/noncopyable.hpp>
#include <libconfig.h++>
#include <string>

namespace BBCP {
    namespace App {

class ConfigLock;

class Config {
public:
    friend class BBCP::App::ConfigLock;

    Config(std::string const &file) :
        cfg(new libconfig::Config()),
        mutex(new boost::mutex())
    {
        cfg->readFile(file.c_str());
    }

private:
    boost::shared_ptr<libconfig::Config> cfg;
    boost::shared_ptr<boost::mutex> mutex;
};

class Server;
class Client;

class ConfigLock : boost::noncopyable {
public:
    ConfigLock(BBCP::App::Config const &wrapper) :
        cfg(wrapper.cfg),
        mutex(wrapper.mutex),
        lock(new LockType(*mutex))
    { }

    libconfig::Config &get() throw() { return *cfg; };
private:
    boost::shared_ptr<libconfig::Config> cfg;
    boost::shared_ptr<boost::mutex> mutex;

    typedef boost::lock_guard<boost::mutex> LockType;
    boost::shared_ptr<LockType> lock;
};

    }
}

#endif

对于懒惰的人来说,这个类包括......好吧,两个类(讽刺?):BBCP::App::ConfigBBCP::App::ConfigLockBBCP::App::Config 只是加载一个文件,而BBCP::App::ConfigLockBBCP::App::Config 作为参数,然后锁定BBCP::App::Config 的互斥锁。创建锁后,调用BBCP::App::ConfigLock::get,它返回对libconfig::Config 对象的引用!。

问题

嗯:

server.cpp:

void BBCP::App::Server::startAccept() {
    newClient.reset(new BBCP::App::Client(io_service, config_wrapper));
    acceptor.async_accept(newClient->getSocket(), boost::bind(&BBCP::App::Server::acceptHandler, this, boost::asio::placeholders::error));
}

这个函数创建一个新的客户端对象,加载了boost::asio::io_service对象和BBCP::App::Config对象。

服务器.cpp

void BBCP::App::Server::acceptHandler(boost::system::error_code const &e) {
    if (!acceptor.is_open()) {
        // ARR ERROR!
        return;
    }

    if (!e) {
        client_pool.create_thread(*newClient);
    }
    else {
        // HANDLE ME ERROR
        throw;
    }

    startAccept();
}

此函数创建一个新线程或(尚未实现)错误以防……嗯,错误,然后再次启动接受循环。

在这部分之前,客户端代码大多无关紧要:

client.cpp:

void BBCP::App::Client::parseBody() {
    BBCP::Protocol::Header header;
    BBCP::Protocol::Hello hello;
    boost::scoped_ptr<BBCP::App::ConfigLock> lock;
    libconfig::Config *cfg;

    (...)

    switch ((enum BBCP::Protocol::PacketType)header.type()) {
        case BBCP::Protocol::HELLO:
            (...)

            // config_wrapper is a private variable in the client class!
            lock.reset(new BBCP::App::ConfigLock(config_wrapper));

            // ARRRRRRR HERE BE DRAGOONS!!
            *cfg = lock->get();

            (...)

            lock.reset();
            break;
        (...)

    }

    (...)
}

好吧,说实话,我没想到会出现这种错误:

/usr/include/libconfig.h++: In member function ‘void BBCP::App::Client::parseBody()’:
/usr/include/libconfig.h++:338:13: error: ‘libconfig::Config& libconfig::Config::operator=(const libconfig::Config&)’ is private
client.cpp:64:30: error: within this context
client.cpp:71:21: error: request for member ‘exists’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
client.cpp:77:51: error: request for member ‘lookup’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’

但就是这样,我需要一些方法来解决它 :(。我尝试将 BBCP::App::Client 设为 BBCP::App::ConfigLock 的朋友类,但结果如下:

In file included from ../include/app_config.h:4:0,
                 from ../include/app_main.h:6,
                 from main.cpp:18:
../include/app_client.h:15:53: error: ‘BBCP::App::Config’ has not been declared
In file included from ../include/app_config.h:4:0,
                 from ../include/app_main.h:6,
                 from main.cpp:18:
../include/app_client.h:32:5: error: ‘Config’ in namespace ‘BBCP::App’ does not name a type
In file included from ../include/app_config.h:4:0,
                 from ../include/app_main.h:6,
                 from main.cpp:18:
../include/app_client.h: In constructor ‘BBCP::App::Client::Client(boost::asio::io_service&, const int&)’:
../include/app_client.h:15:120: error: class ‘BBCP::App::Client’ does not have any field named ‘config_wrapper’

然后我像O_O一样去了,所以我放弃了,来到这里,再次寻求一些über C++ guru hackz0r的帮助,并因为试图访问另一个类的私有成员这样的错误行为而受到责骂。

【问题讨论】:

  • 我不明白这部分:“我已经尝试让BBCP::App::Client 成为BBCP::App::ConfigLock 的朋友类”。错误消息是抱怨 BBCP::App::Client 成员函数试图访问 libconfig::Config 的私有方法(重载赋值运算符),所以为了使用朋友类方法,您不需要将 BBCP::App::Client 设为libconfig::Config的朋友班?
  • 小建议,重命名_APP_CONFIG_H_。 §17.4.3.2.1 说Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.
  • 多线程只读访问变量没有问题。我不知道您是否打算在程序运行时修改配置对象,但如果您不这样做,一切都会变得更简单。此外,您应该花一些时间进行设计(为什么配置对象不能公开复制)并了解编译器试图在错误消息中告诉您的内容,因为它们可以帮助您找出问题所在。
  • @ruakh:是的,对不起,我看错了。

标签: c++ multithreading boost boost-asio boost-thread


【解决方案1】:

您可能希望在cfg 中存储指向配置对象的指针,而不是创建副本(并取消引用未初始化的指针):

cfg = &local->get();

【讨论】:

    【解决方案2】:

    首先要弄清楚你是否朝着正确的方向前进,下一步就是到达那里。

    为什么 Config 类型的赋值运算符是私有的?默认情况下,编译器生成的赋值操作符是公共的,所以如果它被声明为私有的,很可能有一个对象不被复制的原因,否则你应该把它设为公共,问题就不再是问题了.

    在添加朋友声明后,您的特定问题似乎表明您错过了包括声明/定义 Config 类型的标头。然后代码中还有一些错误(尚未定义的成员 - 前一个错误的结果?),或者在原始代码中试图访问指针引用的对象而不取消引用它......

    【讨论】:

    • 这个想法不是复制对象,而是存储指向对象的指针。不过,我不知道为什么将复制操作员设为私有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多