Thread: intialize array allocated by new

  1. #1
    Registered User
    Join Date
    Jan 2009
    Posts
    159

    intialize array allocated by new

    Hi,
    I was wondering if there is a single line initializing an array allocated by new?
    Something like this:
    Code:
    int *p = new int[3] {1, 2, 3};
    PS: I know it is wrong.
    Thanks!

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    That's almost exactly the Java syntax, but unfortunately, as far as I know, there's no way to do this in C++.

    If you know the data, you probably don't need to dynamically allocate an array for it, though. Because you can certainly do this, as I'm sure you're aware.
    Code:
    int p[] = {1, 2, 3};
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    If you really needed this type of functionality, you could always look into boost's assignment library. This allows you to write code like:
    Code:
    array<int,4> a = list_of(1)(2)(3)(4).to_array( a );

  4. #4
    Registered User
    Join Date
    May 2009
    Posts
    37
    Quote Originally Posted by bithub View Post
    If you really needed this type of functionality, you could always look into boost's assignment library. This allows you to write code like:
    Code:
    array<int,4> a = list_of(1)(2)(3)(4).to_array( a );
    Holy smokes! That looks to weird for my tastes. I wonder what the efficiency is like.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    A lot of boost's stuff seems to be template magic -- so at a guess, I'd say it's probably reasonably efficient at runtime, but inefficient for compilation time.

    Erm . . . maybe it is inefficient. doubt about boost/multi_array's efficiency [Archive] - CodeGuru Forums
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Well the container type isn't really important, so I don't think there is an efficiency factor here. You can do the same thing with a vector if you want:
    Code:
    vector<int> v = list_of(1)(2)(3)(4).to_container( v );
    Holy smokes! That looks to weird for my tastes.
    Yeah, it takes some getting used to. If you really want to make your brain go crazy for a couple hours, try and follow how the boost developers implement this. It's pretty crazy.

    For the most part, I think its greatest usefulness is in initializing maps. For example:
    Code:
    const map<string,int> dayOfWeekLookup = (map_list_of
        ("monday", 0)
        ("tuesday", 1)
        ("wednesday", 2)
        ("thursday", 3)
        ("friday", 4)
        ("saturday", 5)
        ("sunday", 6));

  7. #7
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    This would probably be better if it returned a vector...

    Code:
    #include <iterator>
    #include <iostream>
    
    using namespace std;
    
    template < typename Type >
    struct block
    {
        block( size_t capacity = 0 )
        : data( new Type[ capacity ] ), size( 0 ), capacity( capacity )
        {    }
    
        block( const block& rhs )
        : data( const_cast< block& >( rhs ).orphan( ) ), size( rhs.size ), capacity( rhs.capacity )
        {    }    
        
        block& operator , ( Type const& value )
        {
            if( size + 1 > capacity )
            {
                Type*
                    saved = data;
                try
                {
                    data = new Type[ capacity = capacity + 1 << 1 ];
                }
                catch( ... )
                {
                    delete [ ] saved;
                    throw;
                }
                copy( saved, saved + size, data );
                delete [ ] saved;
            }
            data[ size++ ] = value;
            return *this;
        }
        
        Type* orphan( void )
        {
            Type*
                saved = data;
            data = 0;    
            return saved;
        }    
        
        inline operator Type* ( void )
        {
            return orphan( );
        }
            
        virtual ~block( void )
        {
            delete [ ] data; 
        }
        
        protected:
        
        Type*
            data;
        size_t
            size, 
            capacity;
    };
    
    int main( void )
    {
        int* 
            array = ( block< int >( ), 1, 2, 3 );
        copy( array, array + 3, ostream_iterator< int, char >( cout, "\n" ) );
        return 0;
    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    This should be more resilient to exceptions.

    Actually, this should use `std::swap_ranges' just in case there exists a non-throwing swap algorithm for the relevant type.

    Soma

    Code:
    block & operator , (Type const & value)
    {
    	if(size + 1 > capacity)
    	{
    		std::size_t new_capacity((capacity + 1) << 1);
    		Type * work(new Type[new_capacity]);
    		std::copy(data, data + size, work);
    		std::swap(data, work);
    		capacity = new_capacity;
    		delete[] work;
    	}
    	data[size++] = value;
    	return *this;
    }
    Last edited by phantomotap; 06-08-2009 at 11:39 PM. Reason: added the obvious suggestion

  9. #9
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    You're not catching the possible bad_alloc (or whatever other) exception at the point of allocation, though, which would result in a memory leak (in 'saved'). Come to think of it, an exception could occur within std::copy (not with POD's, though), so that should really be in a try block, as well.
    Last edited by Sebastiani; 06-09-2009 at 12:17 AM. Reason: clarification
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  10. #10
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    I guess I could have simplified it by using std::auto_ptr.

    Code:
    #include <memory>
    #include <iterator>
    #include <iostream>
    
    using namespace std;
    
    template < typename Type >
    struct block
    {
    	block( size_t capacity = 0 )
    	: data( new Type[ capacity ] ), size( 0 ), capacity( capacity )
    	{	}
    
    	block& operator , ( Type const& value )
    	{
    		if( size + 1 > capacity )
    		{
    			auto_ptr< Type >
    				saved = data;
    			data.reset( new Type[ capacity = capacity + 1 << 1 ] );
    			copy( saved.get( ), saved.get( ) + size, data.get( ) );
    		}
    		data.get( )[ size++ ] = value;
    		return *this;
    	}
    	
    	inline operator Type* ( void )
    	{
    		return data.release( );
    	}
    	
    	protected:
    	
    	auto_ptr< Type >
    		data;
    	size_t
    		size, 
    		capacity;
    };
    
    int main( void )
    {
    	int* 
    		array = ( block< int >( ), 1, 2, 3 );
    	copy( array, array + 3, ostream_iterator< int, char >( cout, "\n" ) );
    	return 0;
    }
    EDIT:
    @Soma: I didn't incorporate any of the changes you suggested yet simply because I wasn't exactly sure what you were getting at.
    Last edited by Sebastiani; 06-09-2009 at 12:45 AM.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    You're not catching the possible bad_alloc (or whatever other) exception at the point of allocation, though, which would result in a memory leak (in 'saved').
    I'm not catching any allocation related exception because I don't care anything about it. (It is the correct approach, not catching an exception when you can't do anything useful with it.) If `operator new []' raises an exception construction never happens and no leak occurs. If a constructor raises an exception, after the allocation succeeds, a conformant compiler will "unwind" the fully constructed object by calling the destructor for each object and freeing the allocated memory. (It only becomes the programmers responsibility after every object in the array is fully constructed.)

    Come to think of it, an exception could occur within std::copy (not with POD's, though), so that should really be in a try block, as well.
    This can't be helped without writing a good deal of code. (You can write an exception neutral version in the face of a copy constructor that throws, but most programmers will refuse to pay the high cost associated with such an implementation.) The best bet is to use `std::swap_ranges' and hope it eventually calls a swap method with "Koenig Lookup" enabled. (This, last I looked, isn't mandated by the standard.) If that is the case this implementation is almost as good as it gets. (Unless the implementation uses some insane hack to bypass construction without throwing in the case of an error.) You could of course use a smart pointer that works with arrays to strengthen the exception guarantees for the other case.

    I guess I could have simplified it by using std::auto_ptr.
    That isn't simplified. It is horribly broken. (You must never use an array with `std::auto_ptr<???>'.) It also only serves to hide the problem. If the allocation fails, which as you've indicated is a problem with very real potential, the compiler still has to do the above referenced "magic"--lacking a better work--before the assignment to the `std::auto_ptr<Type>' instance.

    I didn't incorporate any of the changes you suggested yet simply because I wasn't exactly sure what you were getting at.
    My version is exception neutral in the face of a non-throwing copy constructor. (With the additional suggestion it is exception neutral in the face of a copy constructor that can throw an exception.) It can be made exception safe with a simple smart pointer. Your variation is not exception neutral and can't be made exception neutral without a complete shift in the implementation. It isn't exception safe even in the case of a smart pointer.

    Soma

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Of course, I was totally overlooking the fact that 'data' was protected by the destructor from leaking. The std::auto_ptr/array problem slipped my mind, as well. Long night, I guess.

    At any rate, a more orthogonal design altogether might be:

    Code:
    #include <list>
    #include <vector>
    #include <iterator>
    #include <iostream>
    
    using namespace std;
    
    template < typename Type >
    struct values
    {
    	values( void )
    	{	}
    
    	values( values const& rhs )
    	{	
    		data.swap( const_cast< values& >( rhs ).data );
    	}
    
    	values& operator , ( Type const& value )
    	{
    		data.push_back( value );
    		return *this;
    	}
    
    	template < typename Container >
    	inline operator Container( void )
    	{
    		return Container( data.begin( ), data.end( ) );
    	}
    	
    	protected:
    	
    	list< Type >
    		data;
    };
    
    int main( void )
    {
    	vector< int >
    		array = ( values< int >( ), 1, 2, 3 );
    	copy( array.begin( ), array.end( ), ostream_iterator< int, char >( cout, "\n" ) );
    	return 0;
    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  13. #13
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Considering that all the containers have a specialized swap an overload/specialization is in order with a slightly different interface.

    Soma

    Code:
       std::vector<int> vi; {assigner(vi, 3) /* possibly construct an assign_to<???> */, 1, 2 3};
    Code:
       std::list<int> li; {assigner(li, 3), 1, 2 3}; // ignore param2(3)
    Code:
       int * ai; {assigner(ai, 3), 1, 2 3};

  14. #14
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    phantomotap:
    Sebastiani points out the exception safety missing from that example, but here's another way you could add it:
    Code:
    block & operator , (Type const & value)
    {
    	if (size + 1 > capacity)
    	{
    		std::size_t new_capacity((capacity + 1) << 1);
    		struct Dummy {
    			Type *p;
    			Dummy(int c) : p(new Type[c]) {}
    			~Dummy() { delete[] p; }
    		} d(new_capacity);
    		std::copy(data, data + size, d.p);
    		std::swap(data, d.p);
    		capacity = new_capacity;
    	}
    	data[size++] = value;
    	return *this;
    }
    It's a lot nicer when if an exception is thrown it doesn't get caught at a dozen or so different levels, and extraneous catch-all statements are definitely best avoided where possible. Fortunately it requires roughly the same number of lines to make a nested class that does what we need. (D makes this approach even easier FTW!)
    I must say, I've never used the comma operator before though.

    I'd probably just derive from std::vector as well.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    phantomotap
    Sebastiani points out the [...] from that example
    here's another way you could add it
    It's a lot nicer when if an [...] at a dozen or so different levels
    it requires roughly the [...] class that does what we need
    I honestly can't figure out if you are trying to point something out to me or Sebastian. You hailed me, but you've not added anything to the discussion. You've only reiterated bits we've said and I'm not sure what that has to do with either of us. If you were offering something to me, you'll have to be explicit 'cause I don't get it.

    Still, you did add, after a fashion, a smart pointer that works with arrays. I hadn't thought of using a nested class to bypass the work. That was a good bit of sorcery.

    I'd probably just derive from std::vector as well.
    I don't see why. With an almost negligible change in the interface you can bypass additional copies mandated by the assignment and possibly avoid allocation. Or do you intend a different interface?

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 07-11-2008, 07:39 AM
  2. Run-time error with dynamically allocated 3-D array
    By OceanDesigner in forum C Programming
    Replies: 2
    Last Post: 10-21-2005, 02:29 PM
  3. Template Array Class
    By hpy_gilmore8 in forum C++ Programming
    Replies: 15
    Last Post: 04-11-2004, 11:15 PM
  4. passing a 2dim dynamically allocated array to a funct
    By agerealm in forum C++ Programming
    Replies: 3
    Last Post: 03-10-2004, 06:55 PM
  5. Help with an Array
    By omalleys in forum C Programming
    Replies: 1
    Last Post: 07-01-2002, 08:31 AM