I have recently read something like this. I konw it is not so complex. But as a beginner, I just don't know why it works, so here is the description.
#include <list>
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
list<T *> testlist;
A();
~A();
void m_append(T* one);
};
template <typename T>
A<T>::A()
{
cout << "constructor" << endl;
}
template <typename T>
A<T>::~A()
{
cout << "destructor" << endl;
}
template <typename T>
void A<T>::m_append(T* one)
{
cout << *one << " push in" << endl;
testlist.push_back(one);
}
int main(void)
{
A<int> a;
int b = 4;
int c = 5;
int d = 6;
a.m_append(&b);
a.m_append(&c);
a.m_append(&d);
return 0;
}
In my opinion this testlist is not initialized, there should be something wrong.
But it works.
constructor
4 push in
4
5 push in
5
6 push in
6
destructor
So I am pretty confused. There is no need to initialize this testlist or?
Data member testlist is not mentioned in the member initializer list of constructor of A, and doesn't have default member initializer (since C++11), then it would be default initialized via the default constructor of std::list.
when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.
And
There is no need to initialize this testlist or?
It depends on your intent. If the default initialization is enough, then yes.
To understand this, you must know how a list is constructed.
It has a control block on the stack and then allocates elements on the heap if you insert elements. The control block is initialized by the constructor.
Here is a picture to understand how this looks like:
So you don't need to initialize the list.
Related
This question already has answers here:
Copy constructor of template class
(2 answers)
What is a converting constructor in C++ ? What is it for?
(3 answers)
Closed 1 year ago.
I am doing a course C++ Fundamentals for Professionals on educative.io website, it mentioned the above statement added in the question,
I created a small c++ program to experiment the same -
#include<iostream>
using namespace std;
template<typename T1>
class MyTemplateClass
{
public:
T1 data;
MyTemplateClass()
{
cout << "in default const" << endl;
}
MyTemplateClass(const MyTemplateClass& other)
{
cout << "in default copy const - created automatically" << endl;
this->data = other.data;
}
template<typename T>
MyTemplateClass(const MyTemplateClass<T>& other)
{
cout << "in templated copy const" << endl;
this->data = other.data;
}
};
int main()
{
cout << "hello world" << endl;
MyTemplateClass<int> obj;
MyTemplateClass<double> obj2(obj);
MyTemplateClass<int> obj3(obj);
}
For obj3 it calls the default copy constructor which makes sense but for obj2 it calls the templated copy constructor, so I see a use for the constructor that if I have a class that takes T1 = double, we can created an object for it from an object of a class that takes T1 = int.
Does obj2 using template MyTemplateClass(const MyTemplateClass& other) - this function doesn't seem like a valid copy constructor according to the rules of a copy constructor defined in c++ but why is it such a case - since this seems like a valid use case?
A constructor template doesn't count as copy constructor even if it has the correct signature of a copy constructor for some template argument.
This is important because if there is no copy constructor declared by the user, then the compiler will declare an implicit one.
So for example
struct A {
A() {}
template<typename T>
A(const T&) { std::cout << "Template called!"; }
};
int main() {
A a;
A b = a;
}
will not output Template called!. Instead of the template specialization the implicitly declared copy constructor (which does nothing in this case) is called.
The following is a class template that implements a stack using an array:
#include <iostream>
using namespace std;
template <typename T>
class stack {
public:
stack (int priv_size) {
T a[priv_size];
top = 0;
}
stack (const stack &s) {
T b[priv_size];
for (top=0; top<s.priv_size; top++) {
b[top] = s.a[top];
}
top++;
}
~stack () {}
const stack& operator = (const stack &s) {
T b[s.priv_size];
for (top=0; top<s.priv_size; top++) {
b[top] = s.a[top];
}
top++;
}
bool empty () {
return top == 0;
}
void push (const T &x) {
a[top++] = x;
}
T pop () {
T c = a[--top];
return c;
}
int size () {
return priv_size;
}
friend ostream& operator << (ostream &out, const stack &s) {
int i;
out << "[";
for (i=0; i<s.top-1; i++) {
out << s.a[i] << ", ";
}
if (i == s.top-1) out << s.a[s.top-1];
out << "]";
return out;
}
private:
int priv_size;
int top;
T a[];
};
int main () {
stack<int> s(10);
cout << "stack s is empty: " << s << endl;
s.push(42);
s.push(17);
cout << "stack s has 2 elements: " << s << endl;
cout << "Removing " << s.pop() << " from the stack..." << endl;
return 0;
}
However as i was trying out the different methods of the class in main I realized that although priv_size is initialized to the value of 10 here: stack<int> s(10); it loses its value right after the constuctor is called.
When i tried debugging in my IDE i realized that once the constructor stack (int priv_size) is called it created another variable priv_size that is initialized to the value of 10 instead of using the priv_size member of the class. The previous can also be seen here:
class member priv_size memory address (image)
and here:
unwanted variable priv_size memory address (image)
where the two variables are stored in different memory slots.
Why are the two variables different and why is the one in the second image created in the first place?
I have also tried implementing the class methods outside of the class, like this:
stack<T>::stack (int priv_size) {
T a[priv_size];
top = 0;
}
but i get these two error messages:
Member declaration not found
Type 'T' could not be resolved
What is going on here?
Thanks in advance.
PS: I am aware there are a couple very similar questions posted already, but the answers to those do not seem to fit my problem as my issue seems to be in the class itself and not in main or any other function.
Here are two links to some similar questions:
C++ Class members lose values assigned in a member function
Losing a data member of a base class after the constructor is called
stack (int priv_size) {
T a[priv_size];
top = 0;
}
Problem 1: Your constructor doesn't initialise the member a. It creates a local array.
Problem 2: The size of the array is not a compile time constant. The program is ill-formed.
Problem 3: You don't initialise the priv_size member either.
private:
int priv_size;
int top;
T a[];
Problem 4: You didn't specify size of the array member a. The program is ill-formed.
although priv_size is initialized to the value of 10 here: stack s(10); it loses its value right after the constuctor is called.
Your assumption is wrong. The constructor doesn't set the value of priv_size member, so no value was lost.
where the two variables are stored in different memory slots.
As far as I can tell, one variable is the parameter of the constructor and the other is the member variable that you didn't initialise.
Type 'T' could not be resolved
What is going on here?
To define a template, you need to use the keyword template and specify the template parameters. Like this for example:
template <typename T>
stack<T>::stack (int priv_size) {
Why are the two variables different and why is the one in the second image created in the first place?
Because you declared two different variables.
Its the same issue as in :
struct foo {
int x;
foo(int a) {
int x = a;
}
};
foos constructor declares a local variable named x which shadows the member x and the member x is not initialized. Same with an array:
struct bar {
int a[5];
bar() {
int a[5];
}
};
The member a and the a in the constructor are two distinct arrays.
This is only answering your question, but there are more problems in your code. When you need an array whose size is only known at runtime you should use std::vector.
There are multiple issues with your code. For instance, T a[priv_size]; requires that priv_size is a compile-time constant. And in stack (const stack &s) {T b[priv_size];, the local variable b goes out of scope when the copy ctor ends, while the member a remains uninitialized.
This question already has answers here:
What is this weird colon-member (" : ") syntax in the constructor?
(14 answers)
Closed 3 years ago.
now I am just really new to C++, currently I have a piece of code below:
#include <iostream>
using namespace std;
template<typename T>
class Test1
{
public:
Test1():var1(1) {
cout << "Constructor1" << endl;
}
Test1(T a):var1(a) {
cout << "Constructor2" << endl;
}
private:
int var1;
};
template<typename T>
class Test2
{
public:
Test2():var2(x) {
cout << "Constructor3" << endl;
};
private:
int x;
Test1<int> var2;
};
int main()
{
Test2<int> var3;
return 0;
}
The output will just be
Constructor2
Constructor3
I wonder why the constructor2 will be called instead of constructor1, since in the class Test2, when creating the object var2, I didn't pass in any parameters, shouldn't the constructor without parameter being called?
Thank you in advance!
The constructor for a variable is determined by its initialization. You have an initialization list on your Test2 constructor
Test2():var2(x) {
so you have to look there for how Test2::var2 is initialized. In the initialization list, you're passing the integer x (that's not initialized to anything), so it calls the Test1 constructor that takes an integer.
If you don't explicitly initialize var2 in your initializer list (for example, if you just assign to it in the constructor body), then it will be default initialized and say "constructor 1" instead of 2
Trying to use a unique_ptr inside a union gives me a segfault when I try to std::move or std::make_unique it.
#include <iostream>
#include <memory>
union myUnion{
struct{std::unique_ptr<float> upFloat;}structUpFloat;
struct{std::unique_ptr<int> upInt;}structUpInt;
myUnion(){}
~myUnion(){}
};
struct myStruct{
int x;
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.x = 1;
bStruct.x = 2;
auto upF = std::make_unique<float>(3.14);
auto upI = std::make_unique<int>(3);
aStruct.num.structUpFloat.upFloat = std::move(upF);
bStruct.num.structUpInt.upInt = std::move(upI);
std::cout << "aStruct float = " << *aStruct.num.structUpFloat.upFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.structUpInt.upInt << std::endl;
return 0;
}
However, using a normal pointer works as expected:
#include <iostream>
#include <memory>
union myUnion{
struct{float *pFloat;}structPFloat;
struct{int *pInt;}structPInt;
myUnion(){}
~myUnion(){}
};
struct myStruct{
int x;
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.x = 1;
bStruct.x = 2;
auto upF = std::make_unique<float>(3.14);
auto upI = std::make_unique<int>(3);
aStruct.num.structPFloat.pFloat = upF.get();
bStruct.num.structPInt.pInt = upI.get();
std::cout << "aStruct float = " << *aStruct.num.structPFloat.pFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.structPInt.pInt << std::endl;
return 0;
}
This is using clang.3.4.2 or gcc.4.9.0. So I'm assuming that I am doing something wrong here. Any help would be appreciated.
EDIT:
Ok, so it's probably a nice thing to do to share the code I settled on. Big thanks to everyone who pointed me to managing the lifetime of my pointers in variant members using placement new.
#include <memory>
#include <iostream>
#include <vector>
struct myStruct
{
public:
union
{
std::unique_ptr<float> upFloat;
std::unique_ptr<int> upInt;
};
enum class unionType {f, i,none} type = unionType::none; // Keep it sane
myStruct(){}
myStruct(std::unique_ptr<float> p)
{
new (&upFloat) std::unique_ptr<float>{std::move(p)};
type = unionType::f;
}
myStruct(std::unique_ptr<int> p)
{
new (&upInt) std::unique_ptr<int>{std::move(p)};
type = unionType::i;
}
~myStruct()
{
switch (type)
{
case unionType::f: upFloat.~unique_ptr<float>(); break;
case unionType::i: upInt.~unique_ptr<int>(); break;
}
}
};
int main()
{
std::vector<std::unique_ptr<myStruct>> structVec;
structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(3.14f)));
structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(739)));
structVec.push_back(std::make_unique<myStruct>());
structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(8.95f)));
structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(3)));
structVec.push_back(std::make_unique<myStruct>());
for(auto &a: structVec)
{
if(a->type == myStruct::unionType::none)
{
std::cout << "Struct Has Unallocated Union" << std::endl;
}
else if(a->type == myStruct::unionType::f)
{
std::cout << "Struct float = " << *a->upFloat << std::endl;
}
else
{
std::cout << "Struct int = " << *a->upInt << std::endl;
}
std::cout << std::endl;
}
return 0;
}
Outputs:
Struct float = 3.14
Struct int = 739
Struct Has Unallocated Union
Struct float = 8.95
Struct int = 3
Struct Has Unallocated Union
Changing the active member of a union requires special care to object lifetime. The C++ Standard says (9.5p4):
Note: In general, one must use explicit destructor calls and placement new operators to change the active
member of a union.
When the members are plain old data, it generally "just works", even though you aren't calling constructors (using placement new) and destructors. That's because the lifetime for objects with trivial initialization begins "when storage is obtained" of sufficient size and correct alignment, and the union provides that.
Now you've got members with non-trivial constructor and destructor. Their lifetime doesn't begin when storage is obtained, you also have to cause initialization to finish. And that means placement new. Skipping destructor calls isn't safe either, you get undefined behavior if those destructors would have had side effects your program relies on (and a unique_ptr destructor has the side effect of deallocating its target).
Thus you are calling a move-assignment operator on a member whose lifetime hasn't begun. That is undefined behavior.
For unrestricted union, you have to manage yourself some construct/destruction.
Following may help:
union myUnion{
std::unique_ptr<float> upFloat;
std::unique_ptr<int> upInt;
myUnion(){ new (&upFloat) std::unique_ptr<float>{};}
~myUnion() {}
};
class myStruct
{
public:
~myStruct()
{
destroy();
}
void destroy()
{
switch (type)
{
case unionType::f: num.upFloat.~unique_ptr<float>(); break;
case unionType::i: num.upInt.~unique_ptr<int>(); break;
}
}
void set(std::unique_ptr<int> p)
{
destroy();
new (&num.upInt) std::unique_ptr<int>{std::move(p)};
type = unionType::i;
}
void set(std::unique_ptr<float> p)
{
destroy();
new (&num.upFloat) std::unique_ptr<float>{std::move(p)};
type = unionType::f;
}
public:
enum class unionType {f, i} type = unionType::f; // match the default constructor of enum
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.set(std::make_unique<float>(3.14f));
bStruct.set(std::make_unique<int>(3));
std::cout << "aStruct float = " << *aStruct.num.upFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.upInt << std::endl;
return 0;
}
In C++17, you may use std::variant instead of your own struct
From this reference:
If a union contains a non-static data member with a non-trivial special member function (copy/move constructor, copy/move assignment, or destructor) that function is deleted by default in the union and needs to be defined explicitly by the programmer.
I assume that the reason you wrapped the pointers in simple structures is because you could not build it otherwise, due to the restrictions imposed by the above paragraph.
What you have done instead is bypassed the compilers safety-guards, and probably have undefined behavior in your code.
From ยง12.6.2[class.base.init]/p8 of the standard (emphasis added):
In a non-delegating constructor, if a given non-static data member or
base class is not designated by a
mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
otherwise, if the entity is a variant member (9.5), no initialization is performed;
[...]
Union members are variant members, which means that the unique_ptrs are left uninitialized. In particular, no constructor, not even the default one, is called. Technically, the lifetime of these unique_ptrs never even began.
The unique_ptr move assignment operator must delete what the unique_ptr is currently holding, but you are move-assigning to an uninitialized "unique_ptr" containing garbage values. As a result, your move assignment likely caused an attempt to delete a garbage pointer, causing a segfault.
I have a class Model:
class Model
{
...
boost::shared_ptr<Deck> _deck;
boost::shared_ptr<CardStack> _stack[22];
};
Deck inherits from CardStack.
I tried to make _stack[0] point to the same thing that _deck points to by going:
{
_deck = boost::shared_ptr<Deck>(new Deck());
_stack[0] = _deck;
}
It seems that the assignment to _deck of _stack[0] results in a copy of _deck being made. (I know this because modifications to _stack[0] do not result in modifications to _deck.) How can I get them to point to the same thing?
Ok - no copy constructor is being called. I have verified this by implementing it and seeing if it gets called - it doesn't.
However - I have a function that operates on CardStack objects:
void TransferSingleCard(CardStack & src, CardStack & dst, Face f)
{
if( !src._cards.empty() )
{
src._cards.back().SetFace(f);
dst.PushCard(src._cards.back());
src._cards.pop_back();
}
}
Now - when I call:
{
TransferSingleCard(*_stack[DECK], _someotherplace, FACEDOWN);
std::cout << *_stack[DECK];
std::cout << *_deck;
}
I get this output (where std::cout on a CardStack will print out the size of that stack):
Num(103) TOP
Num(104) TOP
... so I've concluded (incorrectly?) that _stack[DECK] points to something different.
The Deck
class Deck : public CardStack
{
public:
Deck(int numsuits=2, StackIndex index = NO_SUCH_STACK );
Deck::Deck( const Deck & d);
int DealsLeft() const;
void RecalcDealsLeft();
private:
int _dealsleft;
};
Not clear what you are asking about - consider this code:
#include <iostream>
#include "boost/shared_ptr.hpp"
using namespace std;
struct A {
virtual ~A() {
cout << "destroyed" << endl;
}
};
struct B : public A {
};
int main() {
boost::shared_ptr<B> b( new B );
boost::shared_ptr<A> a;
a = b;
}
Only one "destroy" message appears, indicating that no copy has been made.
This example - derives from #Neil's answer, tries to emulate what you say is happening. Could you check that it works as expected (A and B have the same count) on your system.
Then we could try and modify this code or your code until they match.
#include <boost/shared_ptr.hpp>
#include <iostream>
class A {
public:
virtual ~A()
{
std::cerr << "Delete A" << std::endl;
}
int _count;
void decrement()
{
_count --;
}
};
class B : public A {
public:
virtual ~B()
{
std::cerr << "Delete B" << std::endl;
}
};
int main()
{
boost::shared_ptr<B> b(new B);
b->_count = 104;
boost::shared_ptr<A> a;
a = b;
a->decrement();
std::cerr << "A:" << a->_count << std::endl;
std::cerr << "B:" << b->_count << std::endl;
return 0;
}
EDIT:
So from the comment, we know the original pointers are correct, so now we need to trace.
Either:
log pointers to see when they change.
Use watchpoints in a debugger to see when the pointer changes.
Use a third shared pointer to see which pointer is changed.
Introduce a function that changes both pointers at the same time.
I think the problem is that you're assigning between different types here. boost::shared_ptr is a template and templates are not polymorphic even if the type in them is. So what's happening is that your compiler sees the assignment from boost::shared_ptr<Deck> to boost::shared_ptr<CardStack> and notices that it can make the assignment by calling the copy constructor for CardStack to duplicate the Deck object.
I think what you want the assignment to look like is something like this:
_stack[0] = boost::static_pointer_cast<CardStack>(_deck);
Which will do the conversion the way you expect it to.
I think you may want shared_array for _stack . . . Take a look at the documentation on shared_ptr;from boost.org, specifically:
http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm
"Normally, a shared_ptr cannot
correctly hold a pointer to a
dynamically allocated array. See
shared_array for that usage."
Also, be aware of the T* get() function (not to be used without good reason) which returns the raw pointer being held by the managed pointer (shared_ptr in this case).