QSharedDataPointer with forward-declared class - c++

The Qt documentation suggests that using the QSharedDataPointer with a visible implementation of its inferior is not typical.
So according to the small example snipped in the docs, I came up with the following source (SSCCE).
The interface: Model.h
The interface is straight forward, just the forward declaration of the private class and the handle class, with copy-ctor and d-tor declared:
#include <QtCore/QSharedDataPointer>
class ModelPrivate;
class Model {
public:
Model();
Model(const Model &other);
~Model();
QSharedDataPointer<ModelPrivate> d;
};
Private header: Model_p.h
Just declares and defines the inferior class.
#include <QSharedData>
class ModelPrivate:
public QSharedData {
public:
};
Implementation: Model.cc
Consisting of the implementation of the c-tors/d-tor, taken from the docs.
#include "Model.h"
#include "Model_p.h"
class ModelPrivate:
public QSharedData {
};
Model::Model():
d(new ModelPrivate()) {
}
Model::Model(const Model &other):
d(other.d) {
}
Model::~Model() {
}
Use case: main.cc
Where it all failed.
#include <QString>
#include "Model.h"
int main(int argc, char *argv[]) {
QString s1, s2;
s2 = s1;
Model m1, m2;
m2 = m1;
}
Just two instances and an assignment, as one would do with any other shared class as well. However, it fails badly because of
invalid use of incomplete type 'class ModelPrivate'
I can't figure out how to make this work the expected way according to the documentation, i.e. without fully declaring the private class in the header as well. I know it works when doing so but I'd like to understand the docs. The example of assigning shared classes is included in the docs as well. From the above-linked docs:
The copy constructor is not strictly required here, because class
EmployeeData is included in the same file as class Employee
(employee.h). However, including the private subclass of QSharedData
in the same file as the public class containing the QSharedDataPointer
is not typical. Normally, the idea is to hide the private subclass of
QSharedData from the user by putting it in a separate file which would
not be included in the public file. In this case, we would normally
put class EmployeeData in a separate file, which would not be included
in employee.h. Instead, we would just predeclare the private subclass
EmployeeData in employee.h this way:
I suppose compilation fails at the operator= that is used when assigning the Model.

You have several major issues with your concept:
You do need to have the private class in separate files to do this nice unlike your original idea.
Regardless that you are writing that you used the concept of the original example from the documentation, you simply did not. You changed the copy constructor concept to copy assighment. Naturally, you need to reimplement that operator, respectively.
Here is my working example of the rewrite for the official example as I think it is better to customize that for the posterity than your diverged example to be more inline with upstream for better comprehension:
main.cpp
#include "employee.h"
int main()
{
Employee e1(1001, "Albrecht Durer");
Employee e2 = e1;
Emplyoee e3;
e3 = e2;
e1.setName("Hans Holbein");
}
employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <QSharedDataPointer>
#include <QString>
class EmployeeData;
class Employee
{
public:
Employee();
Employee(int id, QString name);
Employee(const Employee &other);
Employee& operator =(const Employee &other);
~Employee();
void setId(int id);
void setName(QString name);
int id() const;
QString name() const;
private:
QSharedDataPointer<EmployeeData> d;
};
#endif
employee_p.h
#ifndef EMPLOYEE_P_H
#define EMPLOYEE_P_H
#include <QSharedData>
#include <QString>
class EmployeeData : public QSharedData
{
public:
EmployeeData() : id(-1) { }
EmployeeData(const EmployeeData &other)
: QSharedData(other), id(other.id), name(other.name) { }
~EmployeeData() { }
int id;
QString name;
};
#endif
employee.cpp
#include "employee.h"
#include "employee_p.h"
Employee::Employee()
{
d = new EmployeeData;
}
Employee::Employee(int id, QString name)
{
d = new EmployeeData;
setId(id);
setName(name);
}
Employee::Employee(const Employee &other)
: d (other.d)
{
}
Employee& Employee::operator =(const Employee &other)
{
d = other.d;
return *this;
}
Employee::~Employee()
{
}
void Employee::setId(int id)
{
d->id = id;
}
void Employee::setName(QString name)
{
d->name = name;
}
int Employee::id() const
{
return d->id;
}
QString Employee::name() const
{
return d->name;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
HEADERS += employee.h employee_p.h
SOURCES += main.cpp employee.cpp

The problem is in deed the assignment operator of the d member that is declared and defined via template in the QSharedDataPointer class.
The solution is as trivial as moving the assignment operator of the shared class into the module:
New interface: Model.h
#include <QtCore/QSharedDataPointer>
class ModelPrivate;
class Model {
public:
Model();
Model(const Model &other);
Model &operator =(const Model &other); /* <-- */
~Model();
QSharedDataPointer<ModelPrivate> d;
};
Private header: Model_p.h
#include <QSharedData>
class ModelPrivate:
public QSharedData {
public:
};
Implementation: Model.cc:
#include "Model.h"
#include "Model_p.h"
Model::Model():
d(new ModelPrivate()) {
}
Model::Model(const Model &other):
d(other.d) {
}
Model::~Model() {
}
Model &Model::operator =(const Model &other) {
d = other.d;
return *this;
}
Use case: main.cc
#include <QString>
#include "Model.h"
int main() {
QString s1, s2;
s2 = s1;
Model m1, m2;
m2 = m1;
}
Project file: example.pro
TEMPLATE = app
TARGET = example
QT = core
HEADERS += Model.h Model_p.h
SOURCES += Model.cc main.cc
This in fact is what other classes in the Qt universe itself are implemented like. For example, consider QTextCursor.

Related

C++ Class Object inside another class

It keeps me getting this error in Event.h:
field ‘group’ has incomplete type ‘Group’
For context, I want to have a class Group which has an owner (from class Person) and it consists of a vector of people (class Person):
Group.h
class Person;
#include "Person.h"
Class Group
{
private:
std::string name;
std::vector<Person> people;
int size = 0;
Person owner;
public:
Group(Person owner);
~Group();
}
In the Person class, I want to have just a vector of lists (class List, not important for this specific error). Note that in the Person class I have a constructor Person(int id);
In the Event class, I want to have a group of people invited that can be saved as a Group class:
Event.h
class Group;
#include "Group.h"
class Event
{
private:
std::string tittle;
std::string description;
bool locked;
bool checked;
Group group;
public:
Event(std::string tittle);
~Event();
}
Why can't I have a Person owner on my group?
Edit:
I don't know why, but now it works. I guarded everything with just #pragma once and maybe I changed something in the way I compiled. Thanks for all answers :)
You are defining something out of order. Perhaps the #ifdef guards.
This compiles just fine:
class Person {};
class Group
{
private:
std::string name;
std::vector<Person> people;
int size = 0;
Person owner;
public:
Group( Person owr );
~Group();
};
class Event
{
private:
std::string tittle;
std::string description;
bool locked;
bool checked;
Group group;
public:
Event(std::string tittle);
~Event();
};
Godbolt: https://godbolt.org/z/f785vK1dq
The following works fine for me (Online Demo):
Person.h
#ifndef Person_H
#define Person_H
class Person
{
};
#endif
Group.h
#ifndef Group_H
#define Group_H
#include "Person.h"
#include <string>
#include <vector>
class Group
{
private:
std::string name;
std::vector<Person> people;
int size = 0;
Person owner;
public:
Group(Person owner) : owner(owner) {}
};
#endif
Event.h
#ifndef Event_H
#define Event_H
#include "Group.h"
#include <string>
class Event
{
private:
std::string tittle;
std::string description;
bool locked = false;
bool checked = false;
Group group;
public:
Event(std::string tittle) : tittle(tittle), group(Person{}) {}
};
#endif
main.cpp
#include <iostream>
#include "Event.h"
int main()
{
Event evt("title");
return 0;
}

class function inaccessible c++(beginner problem)

Hello so i want to create a header file class which name testing and also its cpp but for some reason this is inaccessible i dont know why
testing.h
#ifndef TESTING_H
#define TESTING_H
#include <string>
using namespace std;
class testing
{
string Name;
void printname(string name);
};
#endif
testing.cpp
#include <iostream>
#include "testing.h"
using namespace std;
void testing::printname(string name) // inaccessible in my main i dont know what reason :(
{
Name = name;
cout<<Name<<endl;
}
main
#include <iostream>
#include "testing.h"
using namespace std;
using std::string;
int main()
{
testing tester;
tester.printname("JPR"); //error since testing::printname is inaccessible no idea
return 0;
}
If you don't specify the visibility of the members, they are private.
You can either use a struct (visibility is public):
struct testing
{
string Name;
void printname(string name);
};
or you can specify that printname is public:
class testing
{
public:
void printname(string name);
private:
string Name;
};
Try the following:
testing.h
#ifndef TESTING_H
#define TESTING_H
#include <string>
class testing
{
public:
// Better to pass the parameter as const reference to avoid performing a copy.
void printname(const std::string& name);
private:
std::string Name;
};
#endif
testing.cpp
#include <iostream>
#include "testing.h"
void testing::printname(const std::string& name)
{
Name = name;
std::cout << Name << std::endl;
}
main.cpp
#include "testing.h"
int main()
{
testing tester;
tester.printname("JPR");
return 0;
}
In C++:
A class defined with the keyword class has private access for its members and its base classes by default.
Add public: access modifier to your class defition, to mark method as public:
class testing
{
string Name;
public:
void printname(string name);
};
when you create a class, every member function and member variable is set in default as private, which means that they won't be accessible. To make your function public you need to change this in your code:
class testing
{
private: //is private in default, i add it for better readabilty
string Name;
public:
void printname(string name);
};
Worth to mention that you (almost) ALWAYS want to keep all member variables private!

Unit test noncopyable object

I have a noncopyable monster base class, I also have a IView class.
I have a hobgoblin class that inherits from both monster an IView ,
I have a controller that takes a pointer to IView as a parameter.
Basically I want to check if hobgoblin exploded.
I'm using gmock / gtest
I keep getting
Actual function call count doesn't match EXPECT_CALL(h, Explode())...
Expected: to be called at least once
Actual: never called - unsatisfied and active
when i use the mock object. What am i missing?
Monster Base
#ifndef MONSTER_H
#define MONSTER_H
#include <string>
// interface for all monsters
class monster {
public:
virtual ~monster();
// forbid copying
monster(monster const &) = delete;
monster & operator=(monster const &) = delete;
void receive_damage(double damage);
void interact_with_chainsaw();
std::string name() const;
protected:
// allow construction for child classes only
monster();
private:
virtual void do_receive_damage(double damage) = 0;
virtual void do_interact_with_chainsaw() = 0;
virtual std::string do_name() const = 0;
};
#endif // MONSTER_H
IView
#ifndef IVIEW_H
#define IVIEW_H
class IView
{
public:
virtual void Explode() = 0;
virtual ~IView(){}
};
#endif // IVIEW_H
Hobgoblin
#ifndef HOBGOBLIN_H
#define HOBGOBLIN_H
#include "monster.h"
#include "iview.h"
class hobgoblin : public monster, public IView
{
public:
hobgoblin();
void Explode();
virtual ~hobgoblin();
private:
void do_receive_damage(double damage) final;
void do_interact_with_chainsaw() final;
std::string do_name() const final;
double health_;
};
#endif // HOBGOBLIN_H
#include "hobgoblin.h"
#include <QDebug>
hobgoblin::hobgoblin() :
health_(100.0)
{
}
hobgoblin::~hobgoblin()
{
}
void hobgoblin::Explode()
{
health_ = 0;
qDebug() << "Health is 0";
}
void hobgoblin::do_receive_damage(double damage)
{
health_ -= damage;
}
void hobgoblin::do_interact_with_chainsaw()
{
// imagine horrible, gory things here such as
// having to deal with a singleton
}
std::string hobgoblin::do_name() const
{
static std::string const name("Furry hobgoblin of nitwittery +5");
return name;
}
Controller
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "iview.h"
class Controller
{
public:
Controller(IView *view);
void Explode();
~Controller();
private:
IView *m_View;
};
#endif // CONTROLLER_H
#include "controller.h"
#include <QDebug>
Controller::Controller(IView *view):
m_View(view)
{
}
void Controller::Explode()
{
m_View->Explode();
}
Controller::~Controller()
{
}
Unit Test
class mockmonster : public IView
{
public:
MOCK_METHOD0(Explode,void());
virtual ~mockmonster(){}
};
TEST(MockMonster,Explode)
{
// this is not calling explode as expected.
mockmonster h;
Controller c(&h);
c.Explode();
}
TEST(HobGoblin,Explode)
{
// this calls explode fine
hobgoblin h;
Controller c(&h);
c.Explode();
}
Well, shouldn't your Explode function be virtual?
By the looks of it, your mockmonster is shadowing IView's function. Since Controller is taking a pointer to IView, and Explode is non-virtual, it will invoke IView's version.
As a side-note, I doubt if either of your classes being non-copyable matters here. When using gmock, non-copyable classes are problematic when setting up expectations/assertions (i.e. you expect a function to be called with a specific object - this object would have to be copied internally by gmock, and that might fail).

C++ dynamic_cast exception

Please help me to understand strange behavior:
I use dynamic_cast from MyObject to MyLogicObject when a destructor ~MyLogicObject() in processing, but compiler throw an exception: non_rtti_object.
I'm sure that object MyObject is a polymorph type. Where am I wrong?
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <string>
class A
{
int a;
};
class B
{
int b;
};
class MyObject: public A,
public B// if comment this row, and don't use multi inheritable, everything will be fine
{
private: std::string name;
private: bool singleshot;
public: MyObject(void);
public: virtual ~MyObject(void);
protected: void Destroying(void);
public: std::string GetName(void);
public: virtual bool Rename(std::string _newName);
};
#endif
#include "MyObject.h"
#include "MyLogicObject.h"
MyObject::MyObject(void): singleshot(true)
{}
MyObject::~MyObject(void)
{
printf("\n~my object\n");
Destroying();
}
void MyObject::Destroying(void)
{
if(singleshot)
{
printf("\nexception!\n");
dynamic_cast<MyLogicObject*>(this);// exception: non_rtti_object
singleshot = false;
}
}
std::string MyObject::GetName(void)
{
return name;
}
bool MyObject::Rename(std::string _newName)
{
name = _newName;
return true;
}
#ifndef MYLOGICOBJECT_H
#define MYLOGICOBJECT_H
#include "MyObject.h"
class MyLogicObject: public virtual MyObject // if not use virtual inheritance (instead, use the standard inheritance), everything will be fine
{
public: MyLogicObject(void);
public: virtual ~MyLogicObject(void);
public: virtual void Update(float _delta = 0.0f);
// if reimplement virtual method of base class, everything will be fine
/*
public: virtual bool Rename(std::string _newName)
{
return MyObject::Rename(_newName);
}
*/
};
#endif
#include "MyLogicObject.h"
MyLogicObject::MyLogicObject(void)
{}
MyLogicObject::~MyLogicObject(void)
{
printf("\n~my logic object\n");
Destroying();
}
void MyLogicObject::Update(float _delta)
{}
#include <conio.h>
#include <stdio.h>
#include "MyLogicScene.h"
class C
{
int c;
};
class DerivedObject: public MyLogicObject,
public C// if comment this row, and don't use multi inheritable, everything will be fine
{
public: DerivedObject(void)
{}
public: virtual ~DerivedObject(void)
{
printf("~derived object: %s\n", GetName().c_str());
//Destroying(); // if call Destroying in this place, overything will be file
}
};
int main()
{
DerivedObject* object1 = new DerivedObject();
object1->Rename("object1");
printf("delete object1...\n");
delete object1;
getch();
return 0;
}
You are trying to dynamic cast an object of a base class (MyObject) type to the derived class (MyLogicObject). And this conversion is not allowed with dynamic_cast unless the base class is polymorphic and rtti is enabled. See this for reference.
So you basically need to enable rtti in your compiler options.
Once that is done make sure that object1 is a complete object of the derived class (MyLogicObject) for the cast to work without raising an exception.
It would work in the opposite scenario too. If you were trying for example to dynamic cast an object of a derived class (MyLogicObject) type to the base class (MyObject).

Overloaded constructor in derived class

I have the base class Manager and the derived class Worker, the inheritance seem to work properly - I've created a new object of the derived class using it's default constructor and i can output properly.
but now I want to make an overloaded constructor for the derived class (Worker) and there seem to be a compilation error, I tired to look for an answer but I didn't found one.
why the compiles says that Worker doesn't have id, name and salary fields? I've created a derived class by the book and created ctors for it.
Manager header:
#include <string>
#ifndef MANAGER_H
#define MANAGER_H
class Manager
{
public:
Manager (); //ctor
Manager (std::string, std::string, float, int); //overloaded ctor
friend void display (Manager&); //friend function is declared
~Manager (); //dtor
protected:
std::string id;
std::string name;
float salary;
private:
int clearance;
};
Manager cpp:
#include <iostream>
#include "Manager.h"
#include "Worker.h"
Manager::Manager() //default ctor
{
id = "M000000";
name = "blank";
salary = 0;
clearance = 0;
}
Manager::Manager(std::string t_id, std::string t_name, float wage, int num): id (t_id), name (t_name), salary(wage), clearance (num)
{
//overloaded ctor
}
Manager::~Manager()
{
//dtor
}
Worker header:
#include <string>
#ifndef Worker_H
#define Worker_H
class Worker: public Manager
{
public:
Worker();
Worker (std::string, std::string, float, int);
~Worker();
friend void display (Worker&); //friend function is declared
protected:
int projects;
private:
};
#endif // Worker_H
Worker cpp:
#include <iostream>
#include "Manager.h"
#include "Worker.h"
Worker::Worker() //default ctor
{
id = "w000000";
name = " - ";
salary = 0;
projects = 0;
}
Worker::Worker(std::string t_id, std::string t_name, float wage, int num) : id (t_id), name (t_name), salary (wage), projects (num);
{
//ctor
}
Worker::~Worker()
{
//dtor
}
Worker::Worker(std::string t_id, std::string t_name, float wage, int num) : id (t_id), name (t_name), salary (wage), projects (num)
{
//ctor
}
here you initialize the members id,name, salary and clearance defined in base class. you need to pass it to the base class constructor for initialization. you cannot initialize them directly.
id, name and clearance are protected so you can access them in derived class but you cannot initialize them directly using initialization list. either you can initialize inside the constructor or make a call to base constructor in initialization list.
Worker::Worker(std::string t_id, std::string t_name, float wage, int num):Manager(t_id,t_name,wage,0), projects (num)
{
//ctor
}
Derived classes don't see private members of their base class. You have to delegate the constructor.
Your Worker constructors should be like:
Worker::Worker() : Manager("w000000", " - ", 0, 0) {}
Worker::Worker(std::string t_id, std::string t_name, float wage, int num) :
Manager(t_id, t_name, wage, num)
{
}