Interface um std::queue bauen mit nur einem Copyconstructor aufruf des Objektes.



  • Hallo,

    ich möchte um die std::queue ein Interface für das Senden (push) und das Empfangen (front + pop) von Daten machen.

    Das ganze soll auf einem Embedded System laufen, deshalb hätte ich gerne, dass der Kopierkonstruktor nur einmal aufgerufen wird, wenn das Objekt in die Queue gelegt wird. Leider schaffe ich es nicht, da bei mir der Copyconstructor 3x aufgerufen wird. (siehe meinen Code unten)

    Weiß jemand wie ich das ganze eleganter machen kann?

    #include <queue>
    #include <cstddef>
    #include <iostream>
    #include <memory>
    
    /** IQueueSend: Schnittstelle zum versenden von Daten über die Queue.*/
    template<typename T>
    class IQueueSend {
    public:
    	virtual ~IQueueSend(){}
    	virtual inline void send(T data) = 0;
    };
    
    /** IQueueReceive: Schnittstelle zum empfangen von Daten von der Queue.*/
    template<typename T>
    class IQueueReceive {
    public:
    	virtual ~IQueueReceive(){}
    	virtual inline const T receive() = 0;
    };
    
    /** Queue */
    template<typename T>
    class Queue : public IQueueSend<T>, public IQueueReceive<T> {
    private:
    	std::queue<T> queue{};
    
    public:
    	virtual ~Queue(){}
    
    	virtual inline void send(T data) override {
    		queue.push(data);
    	}
    
    	virtual inline const T receive() override {
    		auto data = queue.front();
    		queue.pop();
    		return data;
    	}
    
    	size_t const getSize() {return queue.size();}
    	bool const isEmpty() {return queue.empty();}
    
    };
    
    /** Own data type */
    class MyDataType {
    private:
    	double val{0.0};
    
    public:
    	MyDataType(double val):val{val}{}
    
    	//Kopierkonstruktor
    	MyDataType(const MyDataType &data) {
    		this->val = data.val;
    		std::cout << "Copy MyDataType!" << std::endl;
    	}
    
    	~MyDataType() {
    		std::cout << "MyDataType destroyed!" << std::endl;
    	}
    
    	const double get() const {return val;}
    	void set(const double val) {this->val = val;}
    
    };
    
    int main() {
    	// queue with fundamental data type
    	Queue<unsigned int> queue1;
    	queue1.send(1);
    	queue1.send(2);
    
    	while (!queue1.isEmpty()) {
    		std::cout << "Queue1: " << queue1.receive() << std::endl;
    	}
    
    	// queue with own data type
    	Queue<MyDataType> queue2;
    	MyDataType myData2{2.71828};
    
    	queue2.send(myData2);
    	myData2.set(3.14159);
    
    	while (!queue2.isEmpty()) {
    		std::cout << "Queue2: " << queue2.receive().get() << std::endl;
    	}
    
    	return 1;
    }
    


  • void send(const T& data);
    

    Wenn du allerdings Angst vor einer Kopie hast, solltest du dich fragen, ob virtual sinnvoll ist. Es wird auch nicht besser, wenn man inline dranschreibt.



  • Hallo,

    danke für deine Antwort. Das mit der Referenz war mir schon klar, aber was ist wenn ich als Template Type MyDataType* oder MyDataType& angebe?

    Also meine bevorzugte Lösung wäre wenn ich einen Pointer als Typ angebe, dann soll nur der Pointer kopiert werden und nicht das Objekt selbst.
    Wenn eine Referenz angegeben wird, dann soll das Objekt nur kopiert werden, wenn dieses in die Queue kommt (queue.push()).
    Wenn kein Pointer oder Referenz als Datentyp angegeben wird, dann soll das Objekt wie gehabt kopiert werden.

    Ich habe schon einiges mit Template Spezialisierung herumprobiert, leider komme ich nicht wirklich weiter.

    Hat da jemand Ideen wie dies funktionieren könnte?

    Mein bisheriger Code:

    #include <queue>
    #include <cstddef>
    #include <iostream>
    #include <memory>
    
    /** IQueueSend: Schnittstelle zum versenden von Daten über die Queue.*/
    template<typename T>
    class IQueueSend {
    public:
    	virtual ~IQueueSend(){}
    	virtual void send(const T data) = 0;
    };
    
    /** IQueueReceive: Schnittstelle zum empfangen von Daten von der Queue.*/
    template<typename T>
    class IQueueReceive {
    public:
    	virtual ~IQueueReceive(){}
    	virtual const T receive() = 0;
    };
    
    /** Queue Basisklasse */
    template<typename T>
    class QueueBase : public IQueueSend<T>, public IQueueReceive<T> {
    protected:
    	std::queue<T> queue{};
    
    public:
    	virtual ~QueueBase(){};
    
    	virtual void send(const T data) override {
    		queue.push(data);
    	}
    
    	virtual const T receive() override {
    		static auto data = queue.front();
    		queue.pop();
    		return data;
    	}
    
    	size_t const getSize() {return queue.size();}
    	bool const isEmpty() {return queue.empty();}
    
    };
    
    /* Queue Spezialisierung für elementare Datentypen und Pointer */
    template <typename T>
    class Queue : public QueueBase<T>{};
    
    /* Queue Spezialisierung für Referenzen */
    template <typename T>
    class Queue<T &> : public QueueBase<T> {
    
    protected:
    	std::queue<T> queue{};
    
    public:
    	virtual void send(const T data) override {
    		this->queue.push(data);
    	}
    
    	virtual const T receive() override {
    		static auto data = this->queue.front();
    		this->queue.pop();
    		return data;
    	}
    };
    
    /** Own data type */
    class MyDataType {
    private:
    	double val{0.0};
    public:
    	MyDataType(double val):val{val}{}
    
    	//Kopierkonstruktor
    	MyDataType(const MyDataType &data) {
    		this->val = data.val;
    		std::cout << "Copy MyDataType with the value: " << val << std::endl;
    	}
    
    	~MyDataType() {
    		std::cout << "MyDataType with the value " << val << " destroyed" << std::endl;
    	}
    
    	const double get() const {return val;}
    	void set(const double val) {this->val = val;}
    
    };
    
    int main() {
    	// queue with fundamental data type
    	Queue<unsigned int> queue1;
    	queue1.send(1);
    	queue1.send(5);
    
    	while (!queue1.isEmpty()) {
    		std::cout << "Queue1: " << queue1.receive() << std::endl;
    	}
    
    	// queue with own data type
    	Queue<MyDataType> queue2;
    	MyDataType myData{2};
    
    	queue2.send(myData);
    
    	myData.set(3);
    	while (!queue2.isEmpty()) {
    		std::cout << "Queue2: " << queue2.receive().get() << std::endl;
    	}
    
    	// queue with own data type pointer
    	Queue<MyDataType*> queue3;
    	queue3.send(&myData);
    
    	myData.set(4);
    
    	while (!queue3.isEmpty()) {
    		std::cout << "Queue3: " << queue3.receive()->get() << std::endl;
    	}
    
    	// queue with own data type a reference
    	Queue<MyDataType&> queue4;
    	queue4.send(myData);
    
    	myData.set(5);
    
    	while (!queue4.isEmpty()) {
    		std::cout << "Queue4: " << queue4.receive().get() << std::endl;
    	}
    
    	return 1;
    }
    

Log in to reply